@azure/msal-common 14.4.1-beta.1 → 14.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/dist/account/AccountInfo.d.ts +58 -31
  2. package/dist/account/AccountInfo.d.ts.map +1 -1
  3. package/dist/account/AccountInfo.mjs +64 -0
  4. package/dist/account/AccountInfo.mjs.map +1 -0
  5. package/dist/account/AuthToken.d.ts +17 -17
  6. package/dist/account/AuthToken.mjs +58 -58
  7. package/dist/account/AuthToken.mjs.map +1 -1
  8. package/dist/account/CcsCredential.d.ts +9 -9
  9. package/dist/account/CcsCredential.mjs +8 -8
  10. package/dist/account/CcsCredential.mjs.map +1 -1
  11. package/dist/account/ClientCredentials.d.ts +14 -14
  12. package/dist/account/ClientInfo.d.ts +18 -19
  13. package/dist/account/ClientInfo.d.ts.map +1 -1
  14. package/dist/account/ClientInfo.mjs +37 -37
  15. package/dist/account/ClientInfo.mjs.map +1 -1
  16. package/dist/account/TokenClaims.d.ts +83 -65
  17. package/dist/account/TokenClaims.d.ts.map +1 -1
  18. package/dist/account/TokenClaims.mjs +25 -0
  19. package/dist/account/TokenClaims.mjs.map +1 -0
  20. package/dist/authority/Authority.d.ts +253 -249
  21. package/dist/authority/Authority.d.ts.map +1 -1
  22. package/dist/authority/Authority.mjs +812 -789
  23. package/dist/authority/Authority.mjs.map +1 -1
  24. package/dist/authority/AuthorityFactory.d.ts +31 -31
  25. package/dist/authority/AuthorityFactory.mjs +47 -47
  26. package/dist/authority/AuthorityFactory.mjs.map +1 -1
  27. package/dist/authority/AuthorityMetadata.d.ts +503 -503
  28. package/dist/authority/AuthorityMetadata.mjs +603 -603
  29. package/dist/authority/AuthorityMetadata.mjs.map +1 -1
  30. package/dist/authority/AuthorityOptions.d.ts +27 -27
  31. package/dist/authority/AuthorityOptions.mjs +18 -18
  32. package/dist/authority/AuthorityOptions.mjs.map +1 -1
  33. package/dist/authority/AuthorityType.d.ts +10 -10
  34. package/dist/authority/AuthorityType.mjs +13 -13
  35. package/dist/authority/AuthorityType.mjs.map +1 -1
  36. package/dist/authority/AzureRegion.d.ts +1 -1
  37. package/dist/authority/AzureRegionConfiguration.d.ts +5 -5
  38. package/dist/authority/CloudDiscoveryMetadata.d.ts +5 -5
  39. package/dist/authority/CloudInstanceDiscoveryErrorResponse.d.ts +13 -13
  40. package/dist/authority/CloudInstanceDiscoveryErrorResponse.mjs +8 -8
  41. package/dist/authority/CloudInstanceDiscoveryErrorResponse.mjs.map +1 -1
  42. package/dist/authority/CloudInstanceDiscoveryResponse.d.ts +9 -9
  43. package/dist/authority/CloudInstanceDiscoveryResponse.mjs +8 -8
  44. package/dist/authority/CloudInstanceDiscoveryResponse.mjs.map +1 -1
  45. package/dist/authority/ImdsOptions.d.ts +5 -5
  46. package/dist/authority/OIDCOptions.d.ts +8 -8
  47. package/dist/authority/OpenIdConfigResponse.d.ts +11 -11
  48. package/dist/authority/OpenIdConfigResponse.mjs +10 -10
  49. package/dist/authority/OpenIdConfigResponse.mjs.map +1 -1
  50. package/dist/authority/ProtocolMode.d.ts +8 -8
  51. package/dist/authority/ProtocolMode.mjs +11 -11
  52. package/dist/authority/ProtocolMode.mjs.map +1 -1
  53. package/dist/authority/RegionDiscovery.d.ts +32 -32
  54. package/dist/authority/RegionDiscovery.mjs +106 -106
  55. package/dist/authority/RegionDiscovery.mjs.map +1 -1
  56. package/dist/authority/RegionDiscoveryMetadata.d.ts +6 -6
  57. package/dist/broker/nativeBroker/INativeBrokerPlugin.d.ts +15 -15
  58. package/dist/cache/CacheManager.d.ts +517 -485
  59. package/dist/cache/CacheManager.d.ts.map +1 -1
  60. package/dist/cache/CacheManager.mjs +1264 -1102
  61. package/dist/cache/CacheManager.mjs.map +1 -1
  62. package/dist/cache/entities/AccessTokenEntity.d.ts +25 -25
  63. package/dist/cache/entities/AccountEntity.d.ts +106 -101
  64. package/dist/cache/entities/AccountEntity.d.ts.map +1 -1
  65. package/dist/cache/entities/AccountEntity.mjs +247 -219
  66. package/dist/cache/entities/AccountEntity.mjs.map +1 -1
  67. package/dist/cache/entities/AppMetadataEntity.d.ts +39 -39
  68. package/dist/cache/entities/AppMetadataEntity.mjs +66 -66
  69. package/dist/cache/entities/AppMetadataEntity.mjs.map +1 -1
  70. package/dist/cache/entities/AuthorityMetadataEntity.d.ts +48 -48
  71. package/dist/cache/entities/AuthorityMetadataEntity.mjs +78 -78
  72. package/dist/cache/entities/AuthorityMetadataEntity.mjs.map +1 -1
  73. package/dist/cache/entities/CacheRecord.d.ts +14 -14
  74. package/dist/cache/entities/CacheRecord.mjs +14 -14
  75. package/dist/cache/entities/CacheRecord.mjs.map +1 -1
  76. package/dist/cache/entities/CredentialEntity.d.ts +30 -30
  77. package/dist/cache/entities/IdTokenEntity.d.ts +8 -8
  78. package/dist/cache/entities/RefreshTokenEntity.d.ts +5 -7
  79. package/dist/cache/entities/RefreshTokenEntity.d.ts.map +1 -1
  80. package/dist/cache/entities/ServerTelemetryEntity.d.ts +5 -5
  81. package/dist/cache/entities/ThrottlingEntity.d.ts +13 -13
  82. package/dist/cache/entities/ThrottlingEntity.mjs +23 -23
  83. package/dist/cache/entities/ThrottlingEntity.mjs.map +1 -1
  84. package/dist/cache/interface/ICacheManager.d.ts +166 -166
  85. package/dist/cache/interface/ICachePlugin.d.ts +5 -5
  86. package/dist/cache/interface/ISerializableTokenCache.d.ts +4 -4
  87. package/dist/cache/persistence/TokenCacheContext.d.ts +23 -23
  88. package/dist/cache/persistence/TokenCacheContext.mjs +25 -25
  89. package/dist/cache/persistence/TokenCacheContext.mjs.map +1 -1
  90. package/dist/cache/utils/CacheHelpers.d.ts +64 -64
  91. package/dist/cache/utils/CacheHelpers.d.ts.map +1 -1
  92. package/dist/cache/utils/CacheHelpers.mjs +231 -234
  93. package/dist/cache/utils/CacheHelpers.mjs.map +1 -1
  94. package/dist/cache/utils/CacheTypes.d.ts +69 -67
  95. package/dist/cache/utils/CacheTypes.d.ts.map +1 -1
  96. package/dist/client/AuthorizationCodeClient.d.ts +74 -74
  97. package/dist/client/AuthorizationCodeClient.d.ts.map +1 -1
  98. package/dist/client/AuthorizationCodeClient.mjs +404 -403
  99. package/dist/client/AuthorizationCodeClient.mjs.map +1 -1
  100. package/dist/client/BaseClient.d.ts +51 -51
  101. package/dist/client/BaseClient.mjs +100 -100
  102. package/dist/client/BaseClient.mjs.map +1 -1
  103. package/dist/client/RefreshTokenClient.d.ts +35 -35
  104. package/dist/client/RefreshTokenClient.d.ts.map +1 -1
  105. package/dist/client/RefreshTokenClient.mjs +189 -194
  106. package/dist/client/RefreshTokenClient.mjs.map +1 -1
  107. package/dist/client/SilentFlowClient.d.ts +27 -27
  108. package/dist/client/SilentFlowClient.mjs +118 -118
  109. package/dist/client/SilentFlowClient.mjs.map +1 -1
  110. package/dist/config/AppTokenProvider.d.ts +38 -38
  111. package/dist/config/ClientConfiguration.d.ts +150 -150
  112. package/dist/config/ClientConfiguration.mjs +97 -97
  113. package/dist/config/ClientConfiguration.mjs.map +1 -1
  114. package/dist/constants/AADServerParamKeys.d.ts +52 -0
  115. package/dist/constants/AADServerParamKeys.d.ts.map +1 -0
  116. package/dist/constants/AADServerParamKeys.mjs +60 -0
  117. package/dist/constants/AADServerParamKeys.mjs.map +1 -0
  118. package/dist/crypto/ICrypto.d.ts +58 -58
  119. package/dist/crypto/ICrypto.mjs +30 -30
  120. package/dist/crypto/ICrypto.mjs.map +1 -1
  121. package/dist/crypto/IGuidGenerator.d.ts +4 -4
  122. package/dist/crypto/JoseHeader.d.ts +22 -22
  123. package/dist/crypto/JoseHeader.mjs +37 -37
  124. package/dist/crypto/JoseHeader.mjs.map +1 -1
  125. package/dist/crypto/PopTokenGenerator.d.ts +60 -60
  126. package/dist/crypto/PopTokenGenerator.mjs +82 -82
  127. package/dist/crypto/PopTokenGenerator.mjs.map +1 -1
  128. package/dist/crypto/SignedHttpRequest.d.ts +15 -15
  129. package/dist/error/AuthError.d.ts +44 -44
  130. package/dist/error/AuthError.mjs +46 -46
  131. package/dist/error/AuthError.mjs.map +1 -1
  132. package/dist/error/AuthErrorCodes.d.ts +5 -5
  133. package/dist/error/AuthErrorCodes.mjs +9 -9
  134. package/dist/error/AuthErrorCodes.mjs.map +1 -1
  135. package/dist/error/ClientAuthError.d.ts +237 -237
  136. package/dist/error/ClientAuthError.mjs +249 -249
  137. package/dist/error/ClientAuthError.mjs.map +1 -1
  138. package/dist/error/ClientAuthErrorCodes.d.ts +44 -44
  139. package/dist/error/ClientAuthErrorCodes.mjs +48 -48
  140. package/dist/error/ClientAuthErrorCodes.mjs.map +1 -1
  141. package/dist/error/ClientConfigurationError.d.ts +128 -128
  142. package/dist/error/ClientConfigurationError.mjs +135 -135
  143. package/dist/error/ClientConfigurationError.mjs.map +1 -1
  144. package/dist/error/ClientConfigurationErrorCodes.d.ts +22 -22
  145. package/dist/error/ClientConfigurationErrorCodes.mjs +26 -26
  146. package/dist/error/ClientConfigurationErrorCodes.mjs.map +1 -1
  147. package/dist/error/InteractionRequiredAuthError.d.ts +57 -57
  148. package/dist/error/InteractionRequiredAuthError.d.ts.map +1 -1
  149. package/dist/error/InteractionRequiredAuthError.mjs +77 -78
  150. package/dist/error/InteractionRequiredAuthError.mjs.map +1 -1
  151. package/dist/error/InteractionRequiredAuthErrorCodes.d.ts +5 -6
  152. package/dist/error/InteractionRequiredAuthErrorCodes.d.ts.map +1 -1
  153. package/dist/error/InteractionRequiredAuthErrorCodes.mjs +12 -13
  154. package/dist/error/InteractionRequiredAuthErrorCodes.mjs.map +1 -1
  155. package/dist/error/JoseHeaderError.d.ts +15 -15
  156. package/dist/error/JoseHeaderError.mjs +22 -22
  157. package/dist/error/JoseHeaderError.mjs.map +1 -1
  158. package/dist/error/JoseHeaderErrorCodes.d.ts +2 -2
  159. package/dist/error/JoseHeaderErrorCodes.mjs +6 -6
  160. package/dist/error/JoseHeaderErrorCodes.mjs.map +1 -1
  161. package/dist/error/ServerError.d.ts +7 -7
  162. package/dist/error/ServerError.mjs +14 -14
  163. package/dist/error/ServerError.mjs.map +1 -1
  164. package/dist/index.cjs +8645 -8295
  165. package/dist/index.cjs.map +1 -1
  166. package/dist/index.d.ts +99 -98
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.mjs +7 -3
  169. package/dist/index.mjs.map +1 -1
  170. package/dist/logger/Logger.d.ts +95 -95
  171. package/dist/logger/Logger.mjs +188 -188
  172. package/dist/logger/Logger.mjs.map +1 -1
  173. package/dist/network/INetworkModule.d.ts +29 -29
  174. package/dist/network/INetworkModule.mjs +12 -12
  175. package/dist/network/INetworkModule.mjs.map +1 -1
  176. package/dist/network/NetworkManager.d.ts +33 -33
  177. package/dist/network/NetworkManager.mjs +34 -34
  178. package/dist/network/NetworkManager.mjs.map +1 -1
  179. package/dist/network/RequestThumbprint.d.ts +18 -18
  180. package/dist/network/ThrottlingUtils.d.ts +42 -42
  181. package/dist/network/ThrottlingUtils.mjs +95 -95
  182. package/dist/network/ThrottlingUtils.mjs.map +1 -1
  183. package/dist/packageMetadata.d.ts +2 -2
  184. package/dist/packageMetadata.d.ts.map +1 -1
  185. package/dist/packageMetadata.mjs +4 -4
  186. package/dist/packageMetadata.mjs.map +1 -1
  187. package/dist/request/AuthenticationHeaderParser.d.ts +19 -19
  188. package/dist/request/AuthenticationHeaderParser.mjs +55 -55
  189. package/dist/request/AuthenticationHeaderParser.mjs.map +1 -1
  190. package/dist/request/BaseAuthRequest.d.ts +43 -43
  191. package/dist/request/CommonAuthorizationCodeRequest.d.ts +27 -27
  192. package/dist/request/CommonAuthorizationUrlRequest.d.ts +50 -50
  193. package/dist/request/CommonClientCredentialRequest.d.ts +17 -17
  194. package/dist/request/CommonDeviceCodeRequest.d.ts +21 -21
  195. package/dist/request/CommonEndSessionRequest.d.ts +21 -21
  196. package/dist/request/CommonOnBehalfOfRequest.d.ts +13 -13
  197. package/dist/request/CommonRefreshTokenRequest.d.ts +22 -23
  198. package/dist/request/CommonRefreshTokenRequest.d.ts.map +1 -1
  199. package/dist/request/CommonSilentFlowRequest.d.ts +20 -21
  200. package/dist/request/CommonSilentFlowRequest.d.ts.map +1 -1
  201. package/dist/request/CommonUsernamePasswordRequest.d.ts +17 -17
  202. package/dist/request/NativeRequest.d.ts +19 -19
  203. package/dist/request/NativeSignOutRequest.d.ts +5 -5
  204. package/dist/request/RequestParameterBuilder.d.ts +217 -217
  205. package/dist/request/RequestParameterBuilder.d.ts.map +1 -1
  206. package/dist/request/RequestParameterBuilder.mjs +383 -382
  207. package/dist/request/RequestParameterBuilder.mjs.map +1 -1
  208. package/dist/request/RequestValidator.d.ts +33 -33
  209. package/dist/request/RequestValidator.mjs +81 -81
  210. package/dist/request/RequestValidator.mjs.map +1 -1
  211. package/dist/request/ScopeSet.d.ts +88 -88
  212. package/dist/request/ScopeSet.mjs +197 -197
  213. package/dist/request/ScopeSet.mjs.map +1 -1
  214. package/dist/request/StoreInCache.d.ts +8 -8
  215. package/dist/response/AuthenticationResult.d.ts +41 -41
  216. package/dist/response/AuthorizationCodePayload.d.ts +13 -13
  217. package/dist/response/DeviceCodeResponse.d.ts +25 -25
  218. package/dist/response/ExternalTokenResponse.d.ts +15 -15
  219. package/dist/response/IMDSBadResponse.d.ts +4 -4
  220. package/dist/response/ResponseHandler.d.ts +69 -67
  221. package/dist/response/ResponseHandler.d.ts.map +1 -1
  222. package/dist/response/ResponseHandler.mjs +339 -313
  223. package/dist/response/ResponseHandler.mjs.map +1 -1
  224. package/dist/response/ServerAuthorizationCodeResponse.d.ts +25 -25
  225. package/dist/response/ServerAuthorizationTokenResponse.d.ts +46 -46
  226. package/dist/telemetry/performance/IPerformanceClient.d.ts +51 -51
  227. package/dist/telemetry/performance/IPerformanceMeasurement.d.ts +5 -5
  228. package/dist/telemetry/performance/PerformanceClient.d.ts +191 -191
  229. package/dist/telemetry/performance/PerformanceClient.mjs +409 -409
  230. package/dist/telemetry/performance/PerformanceClient.mjs.map +1 -1
  231. package/dist/telemetry/performance/PerformanceEvent.d.ts +464 -464
  232. package/dist/telemetry/performance/PerformanceEvent.mjs +253 -253
  233. package/dist/telemetry/performance/PerformanceEvent.mjs.map +1 -1
  234. package/dist/telemetry/performance/StubPerformanceClient.d.ts +24 -24
  235. package/dist/telemetry/performance/StubPerformanceClient.mjs +76 -76
  236. package/dist/telemetry/performance/StubPerformanceClient.mjs.map +1 -1
  237. package/dist/telemetry/server/ServerTelemetryManager.d.ts +66 -66
  238. package/dist/telemetry/server/ServerTelemetryManager.mjs +201 -201
  239. package/dist/telemetry/server/ServerTelemetryManager.mjs.map +1 -1
  240. package/dist/telemetry/server/ServerTelemetryRequest.d.ts +8 -8
  241. package/dist/url/IUri.d.ts +12 -12
  242. package/dist/url/UrlString.d.ts +48 -48
  243. package/dist/url/UrlString.mjs +161 -161
  244. package/dist/url/UrlString.mjs.map +1 -1
  245. package/dist/utils/Constants.d.ts +296 -365
  246. package/dist/utils/Constants.d.ts.map +1 -1
  247. package/dist/utils/Constants.mjs +309 -376
  248. package/dist/utils/Constants.mjs.map +1 -1
  249. package/dist/utils/FunctionWrappers.d.ts +27 -27
  250. package/dist/utils/FunctionWrappers.mjs +84 -84
  251. package/dist/utils/FunctionWrappers.mjs.map +1 -1
  252. package/dist/utils/MsalTypes.d.ts +6 -6
  253. package/dist/utils/ProtocolUtils.d.ts +42 -42
  254. package/dist/utils/ProtocolUtils.mjs +69 -69
  255. package/dist/utils/ProtocolUtils.mjs.map +1 -1
  256. package/dist/utils/StringUtils.d.ts +40 -40
  257. package/dist/utils/StringUtils.mjs +95 -95
  258. package/dist/utils/StringUtils.mjs.map +1 -1
  259. package/dist/utils/TimeUtils.d.ts +27 -27
  260. package/dist/utils/TimeUtils.mjs +45 -45
  261. package/dist/utils/TimeUtils.mjs.map +1 -1
  262. package/dist/utils/UrlUtils.d.ts +10 -10
  263. package/dist/utils/UrlUtils.mjs +44 -44
  264. package/dist/utils/UrlUtils.mjs.map +1 -1
  265. package/package.json +93 -92
  266. package/src/account/AccountInfo.ts +96 -0
  267. package/src/account/ClientInfo.ts +2 -3
  268. package/src/account/TokenClaims.ts +28 -0
  269. package/src/authority/Authority.ts +29 -2
  270. package/src/cache/CacheManager.ts +416 -114
  271. package/src/cache/entities/AccountEntity.ts +84 -33
  272. package/src/cache/entities/RefreshTokenEntity.ts +1 -3
  273. package/src/cache/utils/CacheHelpers.ts +1 -6
  274. package/src/cache/utils/CacheTypes.ts +11 -0
  275. package/src/client/AuthorizationCodeClient.ts +4 -5
  276. package/src/client/RefreshTokenClient.ts +3 -15
  277. package/src/client/SilentFlowClient.ts +1 -1
  278. package/src/constants/AADServerParamKeys.ts +56 -0
  279. package/src/crypto/PopTokenGenerator.ts +1 -1
  280. package/src/error/InteractionRequiredAuthError.ts +0 -2
  281. package/src/error/InteractionRequiredAuthErrorCodes.ts +0 -1
  282. package/src/index.ts +17 -5
  283. package/src/packageMetadata.ts +1 -1
  284. package/src/request/CommonRefreshTokenRequest.ts +0 -1
  285. package/src/request/CommonSilentFlowRequest.ts +0 -1
  286. package/src/request/RequestParameterBuilder.ts +7 -5
  287. package/src/response/ResponseHandler.ts +95 -27
  288. package/src/utils/Constants.ts +0 -72
@@ -1,1116 +1,1278 @@
1
- /*! @azure/msal-common v14.4.1-beta.1 2023-11-14 */
1
+ /*! @azure/msal-common v14.5.0 2023-12-01 */
2
2
  'use strict';
3
3
  import { Separators, CredentialType, AuthenticationScheme, THE_FAMILY_ID, APP_METADATA, AUTHORITY_METADATA_CONSTANTS } from '../utils/Constants.mjs';
4
4
  import { generateCredentialKey } from './utils/CacheHelpers.mjs';
5
5
  import { ScopeSet } from '../request/ScopeSet.mjs';
6
6
  import { AccountEntity } from './entities/AccountEntity.mjs';
7
7
  import { createClientAuthError } from '../error/ClientAuthError.mjs';
8
+ import { updateAccountTenantProfileData, tenantIdMatchesHomeTenant } from '../account/AccountInfo.mjs';
8
9
  import { extractTokenClaims } from '../account/AuthToken.mjs';
9
10
  import { name, version } from '../packageMetadata.mjs';
11
+ import { getTenantFromAuthorityString } from '../authority/Authority.mjs';
10
12
  import { getAliasesFromStaticSources } from '../authority/AuthorityMetadata.mjs';
11
13
  import { invalidCacheRecord, bindingKeyNotRemoved, multipleMatchingAppMetadata, methodNotImplemented } from '../error/ClientAuthErrorCodes.mjs';
12
14
 
13
- /*
14
- * Copyright (c) Microsoft Corporation. All rights reserved.
15
- * Licensed under the MIT License.
16
- */
17
- /**
18
- * Interface class which implement cache storage functions used by MSAL to perform validity checks, and store tokens.
19
- * @internal
20
- */
21
- class CacheManager {
22
- constructor(clientId, cryptoImpl, logger, staticAuthorityOptions) {
23
- this.clientId = clientId;
24
- this.cryptoImpl = cryptoImpl;
25
- this.commonLogger = logger.clone(name, version);
26
- this.staticAuthorityOptions = staticAuthorityOptions;
27
- }
28
- /**
29
- * Returns all the accounts in the cache that match the optional filter. If no filter is provided, all accounts are returned.
30
- * @param accountFilter - (Optional) filter to narrow down the accounts returned
31
- * @returns Array of AccountInfo objects in cache
32
- */
33
- getAllAccounts(accountFilter) {
34
- const validAccounts = [];
35
- this.getAccountsFilteredBy(accountFilter || {}).forEach((accountEntity) => {
36
- const accountInfo = this.getAccountInfoFromEntity(accountEntity, accountFilter);
37
- if (accountInfo) {
38
- validAccounts.push(accountInfo);
39
- }
40
- });
41
- return validAccounts;
42
- }
43
- /**
44
- * Gets accountInfo object based on provided filters
45
- */
46
- getAccountInfoFilteredBy(accountFilter) {
47
- const allAccounts = this.getAllAccounts(accountFilter);
48
- if (allAccounts.length > 1) {
49
- // If one or more accounts are found, further filter to the first account that has an ID token
50
- return allAccounts.filter((account) => {
51
- return !!account.idTokenClaims;
52
- })[0];
53
- }
54
- else if (allAccounts.length === 1) {
55
- // If only one account is found, return it regardless of whether a matching ID token was found
56
- return allAccounts[0];
57
- }
58
- else {
59
- return null;
60
- }
61
- }
62
- /**
63
- * Returns a single matching
64
- * @param accountFilter
65
- * @returns
66
- */
67
- getBaseAccountInfo(accountFilter) {
68
- const accountEntities = this.getAccountsFilteredBy(accountFilter);
69
- if (accountEntities.length > 0) {
70
- return accountEntities[0].getAccountInfo();
71
- }
72
- else {
73
- return null;
74
- }
75
- }
76
- getAccountInfoFromEntity(accountEntity, accountFilter) {
77
- const accountInfo = accountEntity.getAccountInfo();
78
- const idToken = this.getIdToken(accountInfo);
79
- if (idToken) {
80
- const idTokenClaims = extractTokenClaims(idToken.secret, this.cryptoImpl.base64Decode);
81
- if (this.idTokenClaimsMatchAccountFilter(idTokenClaims, accountFilter)) {
82
- accountInfo.idToken = idToken.secret;
83
- accountInfo.idTokenClaims = idTokenClaims;
84
- return accountInfo;
85
- }
86
- }
87
- return accountInfo;
88
- }
89
- idTokenClaimsMatchAccountFilter(idTokenClaims, accountFilter) {
90
- if (accountFilter) {
91
- if (!!accountFilter.loginHint &&
92
- !this.matchLoginHint(idTokenClaims, accountFilter.loginHint)) {
93
- return false;
94
- }
95
- if (!!accountFilter.sid &&
96
- !this.matchSid(idTokenClaims, accountFilter.sid)) {
97
- return false;
98
- }
99
- }
100
- return true;
101
- }
102
- /**
103
- * saves a cache record
104
- * @param cacheRecord
105
- */
106
- async saveCacheRecord(cacheRecord, storeInCache) {
107
- if (!cacheRecord) {
108
- throw createClientAuthError(invalidCacheRecord);
109
- }
110
- if (!!cacheRecord.account) {
111
- this.setAccount(cacheRecord.account);
112
- }
113
- if (!!cacheRecord.idToken && storeInCache?.idToken !== false) {
114
- this.setIdTokenCredential(cacheRecord.idToken);
115
- }
116
- if (!!cacheRecord.accessToken && storeInCache?.accessToken !== false) {
117
- await this.saveAccessToken(cacheRecord.accessToken);
118
- }
119
- if (!!cacheRecord.refreshToken &&
120
- storeInCache?.refreshToken !== false) {
121
- this.setRefreshTokenCredential(cacheRecord.refreshToken);
122
- }
123
- if (!!cacheRecord.appMetadata) {
124
- this.setAppMetadata(cacheRecord.appMetadata);
125
- }
126
- }
127
- /**
128
- * saves access token credential
129
- * @param credential
130
- */
131
- async saveAccessToken(credential) {
132
- const accessTokenFilter = {
133
- clientId: credential.clientId,
134
- credentialType: credential.credentialType,
135
- environment: credential.environment,
136
- homeAccountId: credential.homeAccountId,
137
- realm: credential.realm,
138
- tokenType: credential.tokenType,
139
- requestedClaimsHash: credential.requestedClaimsHash,
140
- };
141
- const tokenKeys = this.getTokenKeys();
142
- const currentScopes = ScopeSet.fromString(credential.target);
143
- const removedAccessTokens = [];
144
- tokenKeys.accessToken.forEach((key) => {
145
- if (!this.accessTokenKeyMatchesFilter(key, accessTokenFilter, false)) {
146
- return;
147
- }
148
- const tokenEntity = this.getAccessTokenCredential(key);
149
- if (tokenEntity &&
150
- this.credentialMatchesFilter(tokenEntity, accessTokenFilter)) {
151
- const tokenScopeSet = ScopeSet.fromString(tokenEntity.target);
152
- if (tokenScopeSet.intersectingScopeSets(currentScopes)) {
153
- removedAccessTokens.push(this.removeAccessToken(key));
154
- }
155
- }
156
- });
157
- await Promise.all(removedAccessTokens);
158
- this.setAccessTokenCredential(credential);
159
- }
160
- /**
161
- * Retrieve accounts matching all provided filters; if no filter is set, get all accounts
162
- * Not checking for casing as keys are all generated in lower case, remember to convert to lower case if object properties are compared
163
- * @param accountFilter - An object containing Account properties to filter by
164
- */
165
- getAccountsFilteredBy(accountFilter) {
166
- const allAccountKeys = this.getAccountKeys();
167
- const matchingAccounts = [];
168
- allAccountKeys.forEach((cacheKey) => {
169
- if (!this.isAccountKey(cacheKey, accountFilter.homeAccountId, accountFilter.tenantId)) {
170
- // Don't parse value if the key doesn't match the account filters
171
- return;
172
- }
173
- const entity = this.getAccount(cacheKey);
174
- if (!entity) {
175
- return;
176
- }
177
- if (!!accountFilter.homeAccountId &&
178
- !this.matchHomeAccountId(entity, accountFilter.homeAccountId)) {
179
- return;
180
- }
181
- if (!!accountFilter.localAccountId &&
182
- !this.matchLocalAccountId(entity, accountFilter.localAccountId)) {
183
- return;
184
- }
185
- if (!!accountFilter.username &&
186
- !this.matchUsername(entity, accountFilter.username)) {
187
- return;
188
- }
189
- if (!!accountFilter.environment &&
190
- !this.matchEnvironment(entity, accountFilter.environment)) {
191
- return;
192
- }
193
- if (!!accountFilter.realm &&
194
- !this.matchRealm(entity, accountFilter.realm)) {
195
- return;
196
- }
197
- // tenantId is another name for realm
198
- if (!!accountFilter.tenantId &&
199
- !this.matchRealm(entity, accountFilter.tenantId)) {
200
- return;
201
- }
202
- if (!!accountFilter.nativeAccountId &&
203
- !this.matchNativeAccountId(entity, accountFilter.nativeAccountId)) {
204
- return;
205
- }
206
- if (!!accountFilter.authorityType &&
207
- !this.matchAuthorityType(entity, accountFilter.authorityType)) {
208
- return;
209
- }
210
- if (!!accountFilter.name &&
211
- !this.matchName(entity, accountFilter.name)) {
212
- return;
213
- }
214
- matchingAccounts.push(entity);
215
- });
216
- return matchingAccounts;
217
- }
218
- /**
219
- * Returns true if the given key matches our account key schema. Also matches homeAccountId and/or tenantId if provided
220
- * @param key
221
- * @param homeAccountId
222
- * @param tenantId
223
- * @returns
224
- */
225
- isAccountKey(key, homeAccountId, tenantId) {
226
- if (key.split(Separators.CACHE_KEY_SEPARATOR).length < 3) {
227
- // Account cache keys contain 3 items separated by '-' (each item may also contain '-')
228
- return false;
229
- }
230
- if (homeAccountId &&
231
- !key.toLowerCase().includes(homeAccountId.toLowerCase())) {
232
- return false;
233
- }
234
- if (tenantId && !key.toLowerCase().includes(tenantId.toLowerCase())) {
235
- return false;
236
- }
237
- // Do not check environment as aliasing can cause false negatives
238
- return true;
239
- }
240
- /**
241
- * Returns true if the given key matches our credential key schema.
242
- * @param key
243
- */
244
- isCredentialKey(key) {
245
- if (key.split(Separators.CACHE_KEY_SEPARATOR).length < 6) {
246
- // Credential cache keys contain 6 items separated by '-' (each item may also contain '-')
247
- return false;
248
- }
249
- const lowerCaseKey = key.toLowerCase();
250
- // Credential keys must indicate what credential type they represent
251
- if (lowerCaseKey.indexOf(CredentialType.ID_TOKEN.toLowerCase()) ===
252
- -1 &&
253
- lowerCaseKey.indexOf(CredentialType.ACCESS_TOKEN.toLowerCase()) ===
254
- -1 &&
255
- lowerCaseKey.indexOf(CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME.toLowerCase()) === -1 &&
256
- lowerCaseKey.indexOf(CredentialType.REFRESH_TOKEN.toLowerCase()) ===
257
- -1) {
258
- return false;
259
- }
260
- if (lowerCaseKey.indexOf(CredentialType.REFRESH_TOKEN.toLowerCase()) >
261
- -1) {
262
- // Refresh tokens must contain the client id or family id
263
- const clientIdValidation = `${CredentialType.REFRESH_TOKEN}${Separators.CACHE_KEY_SEPARATOR}${this.clientId}${Separators.CACHE_KEY_SEPARATOR}`;
264
- const familyIdValidation = `${CredentialType.REFRESH_TOKEN}${Separators.CACHE_KEY_SEPARATOR}${THE_FAMILY_ID}${Separators.CACHE_KEY_SEPARATOR}`;
265
- if (lowerCaseKey.indexOf(clientIdValidation.toLowerCase()) === -1 &&
266
- lowerCaseKey.indexOf(familyIdValidation.toLowerCase()) === -1) {
267
- return false;
268
- }
269
- }
270
- else if (lowerCaseKey.indexOf(this.clientId.toLowerCase()) === -1) {
271
- // Tokens must contain the clientId
272
- return false;
273
- }
274
- return true;
275
- }
276
- /**
277
- * Returns whether or not the given credential entity matches the filter
278
- * @param entity
279
- * @param filter
280
- * @returns
281
- */
282
- credentialMatchesFilter(entity, filter) {
283
- if (!!filter.clientId && !this.matchClientId(entity, filter.clientId)) {
284
- return false;
285
- }
286
- if (!!filter.userAssertionHash &&
287
- !this.matchUserAssertionHash(entity, filter.userAssertionHash)) {
288
- return false;
289
- }
290
- /*
291
- * homeAccountId can be undefined, and we want to filter out cached items that have a homeAccountId of ""
292
- * because we don't want a client_credential request to return a cached token that has a homeAccountId
293
- */
294
- if (typeof filter.homeAccountId === "string" &&
295
- !this.matchHomeAccountId(entity, filter.homeAccountId)) {
296
- return false;
297
- }
298
- if (!!filter.environment &&
299
- !this.matchEnvironment(entity, filter.environment)) {
300
- return false;
301
- }
302
- if (!!filter.realm && !this.matchRealm(entity, filter.realm)) {
303
- return false;
304
- }
305
- if (!!filter.credentialType &&
306
- !this.matchCredentialType(entity, filter.credentialType)) {
307
- return false;
308
- }
309
- if (!!filter.familyId && !this.matchFamilyId(entity, filter.familyId)) {
310
- return false;
311
- }
312
- /*
313
- * idTokens do not have "target", target specific refreshTokens do exist for some types of authentication
314
- * Resource specific refresh tokens case will be added when the support is deemed necessary
315
- */
316
- if (!!filter.target && !this.matchTarget(entity, filter.target)) {
317
- return false;
318
- }
319
- // If request OR cached entity has requested Claims Hash, check if they match
320
- if (filter.requestedClaimsHash || entity.requestedClaimsHash) {
321
- // Don't match if either is undefined or they are different
322
- if (entity.requestedClaimsHash !== filter.requestedClaimsHash) {
323
- return false;
324
- }
325
- }
326
- // Access Token with Auth Scheme specific matching
327
- if (entity.credentialType ===
328
- CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME) {
329
- if (!!filter.tokenType &&
330
- !this.matchTokenType(entity, filter.tokenType)) {
331
- return false;
332
- }
333
- // KeyId (sshKid) in request must match cached SSH certificate keyId because SSH cert is bound to a specific key
334
- if (filter.tokenType === AuthenticationScheme.SSH) {
335
- if (filter.keyId && !this.matchKeyId(entity, filter.keyId)) {
336
- return false;
337
- }
338
- }
339
- }
340
- return true;
341
- }
342
- /**
343
- * retrieve appMetadata matching all provided filters; if no filter is set, get all appMetadata
344
- * @param filter
345
- */
346
- getAppMetadataFilteredBy(filter) {
347
- return this.getAppMetadataFilteredByInternal(filter.environment, filter.clientId);
348
- }
349
- /**
350
- * Support function to help match appMetadata
351
- * @param environment
352
- * @param clientId
353
- */
354
- getAppMetadataFilteredByInternal(environment, clientId) {
355
- const allCacheKeys = this.getKeys();
356
- const matchingAppMetadata = {};
357
- allCacheKeys.forEach((cacheKey) => {
358
- // don't parse any non-appMetadata type cache entities
359
- if (!this.isAppMetadata(cacheKey)) {
360
- return;
361
- }
362
- // Attempt retrieval
363
- const entity = this.getAppMetadata(cacheKey);
364
- if (!entity) {
365
- return;
366
- }
367
- if (!!environment && !this.matchEnvironment(entity, environment)) {
368
- return;
369
- }
370
- if (!!clientId && !this.matchClientId(entity, clientId)) {
371
- return;
372
- }
373
- matchingAppMetadata[cacheKey] = entity;
374
- });
375
- return matchingAppMetadata;
376
- }
377
- /**
378
- * retrieve authorityMetadata that contains a matching alias
379
- * @param filter
380
- */
381
- getAuthorityMetadataByAlias(host) {
382
- const allCacheKeys = this.getAuthorityMetadataKeys();
383
- let matchedEntity = null;
384
- allCacheKeys.forEach((cacheKey) => {
385
- // don't parse any non-authorityMetadata type cache entities
386
- if (!this.isAuthorityMetadata(cacheKey) ||
387
- cacheKey.indexOf(this.clientId) === -1) {
388
- return;
389
- }
390
- // Attempt retrieval
391
- const entity = this.getAuthorityMetadata(cacheKey);
392
- if (!entity) {
393
- return;
394
- }
395
- if (entity.aliases.indexOf(host) === -1) {
396
- return;
397
- }
398
- matchedEntity = entity;
399
- });
400
- return matchedEntity;
401
- }
402
- /**
403
- * Removes all accounts and related tokens from cache.
404
- */
405
- async removeAllAccounts() {
406
- const allAccountKeys = this.getAccountKeys();
407
- const removedAccounts = [];
408
- allAccountKeys.forEach((cacheKey) => {
409
- removedAccounts.push(this.removeAccount(cacheKey));
410
- });
411
- await Promise.all(removedAccounts);
412
- }
413
- /**
414
- * Removes the account and related tokens for a given account key
415
- * @param account
416
- */
417
- async removeAccount(accountKey) {
418
- const account = this.getAccount(accountKey);
419
- if (!account) {
420
- return;
421
- }
422
- await this.removeAccountContext(account);
423
- this.removeItem(accountKey);
424
- }
425
- /**
426
- * Removes credentials associated with the provided account
427
- * @param account
428
- */
429
- async removeAccountContext(account) {
430
- const allTokenKeys = this.getTokenKeys();
431
- const accountId = account.generateAccountId();
432
- const removedCredentials = [];
433
- allTokenKeys.idToken.forEach((key) => {
434
- if (key.indexOf(accountId) === 0) {
435
- this.removeIdToken(key);
436
- }
437
- });
438
- allTokenKeys.accessToken.forEach((key) => {
439
- if (key.indexOf(accountId) === 0) {
440
- removedCredentials.push(this.removeAccessToken(key));
441
- }
442
- });
443
- allTokenKeys.refreshToken.forEach((key) => {
444
- if (key.indexOf(accountId) === 0) {
445
- this.removeRefreshToken(key);
446
- }
447
- });
448
- await Promise.all(removedCredentials);
449
- }
450
- /**
451
- * returns a boolean if the given credential is removed
452
- * @param credential
453
- */
454
- async removeAccessToken(key) {
455
- const credential = this.getAccessTokenCredential(key);
456
- if (!credential) {
457
- return;
458
- }
459
- // Remove Token Binding Key from key store for PoP Tokens Credentials
460
- if (credential.credentialType.toLowerCase() ===
461
- CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME.toLowerCase()) {
462
- if (credential.tokenType === AuthenticationScheme.POP) {
463
- const accessTokenWithAuthSchemeEntity = credential;
464
- const kid = accessTokenWithAuthSchemeEntity.keyId;
465
- if (kid) {
466
- try {
467
- await this.cryptoImpl.removeTokenBindingKey(kid);
468
- }
469
- catch (error) {
470
- throw createClientAuthError(bindingKeyNotRemoved);
471
- }
472
- }
473
- }
474
- }
475
- return this.removeItem(key);
476
- }
477
- /**
478
- * Removes all app metadata objects from cache.
479
- */
480
- removeAppMetadata() {
481
- const allCacheKeys = this.getKeys();
482
- allCacheKeys.forEach((cacheKey) => {
483
- if (this.isAppMetadata(cacheKey)) {
484
- this.removeItem(cacheKey);
485
- }
486
- });
487
- return true;
488
- }
489
- /**
490
- * Retrieve the cached credentials into a cacherecord
491
- * @param account {AccountInfo}
492
- * @param request {BaseAuthRequest}
493
- * @param environment {string}
494
- * @param performanceClient {?IPerformanceClient}
495
- * @param correlationId {?string}
496
- */
497
- readCacheRecord(account, request, environment, performanceClient, correlationId) {
498
- const tokenKeys = this.getTokenKeys();
499
- const cachedAccount = this.readAccountFromCache(account);
500
- const cachedIdToken = this.getIdToken(account, tokenKeys, performanceClient, correlationId);
501
- const cachedAccessToken = this.getAccessToken(account, request, tokenKeys, performanceClient, correlationId);
502
- const cachedRefreshToken = this.getRefreshToken(account, false, tokenKeys, performanceClient, correlationId);
503
- const cachedAppMetadata = this.readAppMetadataFromCache(environment);
504
- if (cachedAccount && cachedIdToken) {
505
- cachedAccount.idTokenClaims = extractTokenClaims(cachedIdToken.secret, this.cryptoImpl.base64Decode);
506
- }
507
- return {
508
- account: cachedAccount,
509
- idToken: cachedIdToken,
510
- accessToken: cachedAccessToken,
511
- refreshToken: cachedRefreshToken,
512
- appMetadata: cachedAppMetadata,
513
- };
514
- }
515
- /**
516
- * Retrieve AccountEntity from cache
517
- * @param account
518
- */
519
- readAccountFromCache(account) {
520
- const accountKey = AccountEntity.generateAccountCacheKey(account);
521
- return this.getAccount(accountKey);
522
- }
523
- /**
524
- * Retrieve IdTokenEntity from cache
525
- * @param account {AccountInfo}
526
- * @param tokenKeys {?TokenKeys}
527
- * @param performanceClient {?IPerformanceClient}
528
- * @param correlationId {?string}
529
- */
530
- getIdToken(account, tokenKeys, performanceClient, correlationId) {
531
- this.commonLogger.trace("CacheManager - getIdToken called");
532
- const idTokenFilter = {
533
- homeAccountId: account.homeAccountId,
534
- environment: account.environment,
535
- credentialType: CredentialType.ID_TOKEN,
536
- clientId: this.clientId,
537
- realm: account.tenantId,
538
- };
539
- const idTokens = this.getIdTokensByFilter(idTokenFilter, tokenKeys);
540
- const numIdTokens = idTokens.length;
541
- if (numIdTokens < 1) {
542
- this.commonLogger.info("CacheManager:getIdToken - No token found");
543
- return null;
544
- }
545
- else if (numIdTokens > 1) {
546
- this.commonLogger.info("CacheManager:getIdToken - Multiple id tokens found, clearing them");
547
- idTokens.forEach((idToken) => {
548
- this.removeIdToken(generateCredentialKey(idToken));
549
- });
550
- if (performanceClient && correlationId) {
551
- performanceClient.addFields({ multiMatchedID: idTokens.length }, correlationId);
552
- }
553
- return null;
554
- }
555
- this.commonLogger.info("CacheManager:getIdToken - Returning id token");
556
- return idTokens[0];
557
- }
558
- /**
559
- * Gets all idTokens matching the given filter
560
- * @param filter
561
- * @returns
562
- */
563
- getIdTokensByFilter(filter, tokenKeys) {
564
- const idTokenKeys = (tokenKeys && tokenKeys.idToken) || this.getTokenKeys().idToken;
565
- const idTokens = [];
566
- idTokenKeys.forEach((key) => {
567
- if (!this.idTokenKeyMatchesFilter(key, {
568
- clientId: this.clientId,
569
- ...filter,
570
- })) {
571
- return;
572
- }
573
- const idToken = this.getIdTokenCredential(key);
574
- if (idToken && this.credentialMatchesFilter(idToken, filter)) {
575
- idTokens.push(idToken);
576
- }
577
- });
578
- return idTokens;
579
- }
580
- /**
581
- * Validate the cache key against filter before retrieving and parsing cache value
582
- * @param key
583
- * @param filter
584
- * @returns
585
- */
586
- idTokenKeyMatchesFilter(inputKey, filter) {
587
- const key = inputKey.toLowerCase();
588
- if (filter.clientId &&
589
- key.indexOf(filter.clientId.toLowerCase()) === -1) {
590
- return false;
591
- }
592
- if (filter.homeAccountId &&
593
- key.indexOf(filter.homeAccountId.toLowerCase()) === -1) {
594
- return false;
595
- }
596
- return true;
597
- }
598
- /**
599
- * Removes idToken from the cache
600
- * @param key
601
- */
602
- removeIdToken(key) {
603
- this.removeItem(key);
604
- }
605
- /**
606
- * Removes refresh token from the cache
607
- * @param key
608
- */
609
- removeRefreshToken(key) {
610
- this.removeItem(key);
611
- }
612
- /**
613
- * Retrieve AccessTokenEntity from cache
614
- * @param account {AccountInfo}
615
- * @param request {BaseAuthRequest}
616
- * @param tokenKeys {?TokenKeys}
617
- * @param performanceClient {?IPerformanceClient}
618
- * @param correlationId {?string}
619
- */
620
- getAccessToken(account, request, tokenKeys, performanceClient, correlationId) {
621
- this.commonLogger.trace("CacheManager - getAccessToken called");
622
- const scopes = ScopeSet.createSearchScopes(request.scopes);
623
- const authScheme = request.authenticationScheme || AuthenticationScheme.BEARER;
624
- /*
625
- * Distinguish between Bearer and PoP/SSH token cache types
626
- * Cast to lowercase to handle "bearer" from ADFS
627
- */
628
- const credentialType = authScheme &&
629
- authScheme.toLowerCase() !==
630
- AuthenticationScheme.BEARER.toLowerCase()
631
- ? CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME
632
- : CredentialType.ACCESS_TOKEN;
633
- const accessTokenFilter = {
634
- homeAccountId: account.homeAccountId,
635
- environment: account.environment,
636
- credentialType: credentialType,
637
- clientId: this.clientId,
638
- realm: account.tenantId,
639
- target: scopes,
640
- tokenType: authScheme,
641
- keyId: request.sshKid,
642
- requestedClaimsHash: request.requestedClaimsHash,
643
- };
644
- const accessTokenKeys = (tokenKeys && tokenKeys.accessToken) ||
645
- this.getTokenKeys().accessToken;
646
- const accessTokens = [];
647
- accessTokenKeys.forEach((key) => {
648
- // Validate key
649
- if (this.accessTokenKeyMatchesFilter(key, accessTokenFilter, true)) {
650
- const accessToken = this.getAccessTokenCredential(key);
651
- // Validate value
652
- if (accessToken &&
653
- this.credentialMatchesFilter(accessToken, accessTokenFilter)) {
654
- accessTokens.push(accessToken);
655
- }
656
- }
657
- });
658
- const numAccessTokens = accessTokens.length;
659
- if (numAccessTokens < 1) {
660
- this.commonLogger.info("CacheManager:getAccessToken - No token found");
661
- return null;
662
- }
663
- else if (numAccessTokens > 1) {
664
- this.commonLogger.info("CacheManager:getAccessToken - Multiple access tokens found, clearing them");
665
- accessTokens.forEach((accessToken) => {
666
- void this.removeAccessToken(generateCredentialKey(accessToken));
667
- });
668
- if (performanceClient && correlationId) {
669
- performanceClient.addFields({ multiMatchedAT: accessTokens.length }, correlationId);
670
- }
671
- return null;
672
- }
673
- this.commonLogger.info("CacheManager:getAccessToken - Returning access token");
674
- return accessTokens[0];
675
- }
676
- /**
677
- * Validate the cache key against filter before retrieving and parsing cache value
678
- * @param key
679
- * @param filter
680
- * @param keyMustContainAllScopes
681
- * @returns
682
- */
683
- accessTokenKeyMatchesFilter(inputKey, filter, keyMustContainAllScopes) {
684
- const key = inputKey.toLowerCase();
685
- if (filter.clientId &&
686
- key.indexOf(filter.clientId.toLowerCase()) === -1) {
687
- return false;
688
- }
689
- if (filter.homeAccountId &&
690
- key.indexOf(filter.homeAccountId.toLowerCase()) === -1) {
691
- return false;
692
- }
693
- if (filter.realm && key.indexOf(filter.realm.toLowerCase()) === -1) {
694
- return false;
695
- }
696
- if (filter.requestedClaimsHash &&
697
- key.indexOf(filter.requestedClaimsHash.toLowerCase()) === -1) {
698
- return false;
699
- }
700
- if (filter.target) {
701
- const scopes = filter.target.asArray();
702
- for (let i = 0; i < scopes.length; i++) {
703
- if (keyMustContainAllScopes &&
704
- !key.includes(scopes[i].toLowerCase())) {
705
- // When performing a cache lookup a missing scope would be a cache miss
706
- return false;
707
- }
708
- else if (!keyMustContainAllScopes &&
709
- key.includes(scopes[i].toLowerCase())) {
710
- // When performing a cache write, any token with a subset of requested scopes should be replaced
711
- return true;
712
- }
713
- }
714
- }
715
- return true;
716
- }
717
- /**
718
- * Gets all access tokens matching the filter
719
- * @param filter
720
- * @returns
721
- */
722
- getAccessTokensByFilter(filter) {
723
- const tokenKeys = this.getTokenKeys();
724
- const accessTokens = [];
725
- tokenKeys.accessToken.forEach((key) => {
726
- if (!this.accessTokenKeyMatchesFilter(key, filter, true)) {
727
- return;
728
- }
729
- const accessToken = this.getAccessTokenCredential(key);
730
- if (accessToken &&
731
- this.credentialMatchesFilter(accessToken, filter)) {
732
- accessTokens.push(accessToken);
733
- }
734
- });
735
- return accessTokens;
736
- }
737
- /**
738
- * Helper to retrieve the appropriate refresh token from cache
739
- * @param account {AccountInfo}
740
- * @param familyRT {boolean}
741
- * @param tokenKeys {?TokenKeys}
742
- * @param performanceClient {?IPerformanceClient}
743
- * @param correlationId {?string}
744
- */
745
- getRefreshToken(account, familyRT, tokenKeys, performanceClient, correlationId) {
746
- this.commonLogger.trace("CacheManager - getRefreshToken called");
747
- const id = familyRT ? THE_FAMILY_ID : undefined;
748
- const refreshTokenFilter = {
749
- homeAccountId: account.homeAccountId,
750
- environment: account.environment,
751
- credentialType: CredentialType.REFRESH_TOKEN,
752
- clientId: this.clientId,
753
- familyId: id,
754
- };
755
- const refreshTokenKeys = (tokenKeys && tokenKeys.refreshToken) ||
756
- this.getTokenKeys().refreshToken;
757
- const refreshTokens = [];
758
- refreshTokenKeys.forEach((key) => {
759
- // Validate key
760
- if (this.refreshTokenKeyMatchesFilter(key, refreshTokenFilter)) {
761
- const refreshToken = this.getRefreshTokenCredential(key);
762
- // Validate value
763
- if (refreshToken &&
764
- this.credentialMatchesFilter(refreshToken, refreshTokenFilter)) {
765
- refreshTokens.push(refreshToken);
766
- }
767
- }
768
- });
769
- const numRefreshTokens = refreshTokens.length;
770
- if (numRefreshTokens < 1) {
771
- this.commonLogger.info("CacheManager:getRefreshToken - No refresh token found.");
772
- return null;
773
- }
774
- // address the else case after remove functions address environment aliases
775
- if (numRefreshTokens > 1 && performanceClient && correlationId) {
776
- performanceClient.addFields({ multiMatchedRT: numRefreshTokens }, correlationId);
777
- }
778
- this.commonLogger.info("CacheManager:getRefreshToken - returning refresh token");
779
- return refreshTokens[0];
780
- }
781
- /**
782
- * Validate the cache key against filter before retrieving and parsing cache value
783
- * @param key
784
- * @param filter
785
- */
786
- refreshTokenKeyMatchesFilter(inputKey, filter) {
787
- const key = inputKey.toLowerCase();
788
- if (filter.familyId &&
789
- key.indexOf(filter.familyId.toLowerCase()) === -1) {
790
- return false;
791
- }
792
- // If familyId is used, clientId is not in the key
793
- if (!filter.familyId &&
794
- filter.clientId &&
795
- key.indexOf(filter.clientId.toLowerCase()) === -1) {
796
- return false;
797
- }
798
- if (filter.homeAccountId &&
799
- key.indexOf(filter.homeAccountId.toLowerCase()) === -1) {
800
- return false;
801
- }
802
- return true;
803
- }
804
- /**
805
- * Retrieve AppMetadataEntity from cache
806
- */
807
- readAppMetadataFromCache(environment) {
808
- const appMetadataFilter = {
809
- environment,
810
- clientId: this.clientId,
811
- };
812
- const appMetadata = this.getAppMetadataFilteredBy(appMetadataFilter);
813
- const appMetadataEntries = Object.keys(appMetadata).map((key) => appMetadata[key]);
814
- const numAppMetadata = appMetadataEntries.length;
815
- if (numAppMetadata < 1) {
816
- return null;
817
- }
818
- else if (numAppMetadata > 1) {
819
- throw createClientAuthError(multipleMatchingAppMetadata);
820
- }
821
- return appMetadataEntries[0];
822
- }
823
- /**
824
- * Return the family_id value associated with FOCI
825
- * @param environment
826
- * @param clientId
827
- */
828
- isAppMetadataFOCI(environment) {
829
- const appMetadata = this.readAppMetadataFromCache(environment);
830
- return !!(appMetadata && appMetadata.familyId === THE_FAMILY_ID);
831
- }
832
- /**
833
- * helper to match account ids
834
- * @param value
835
- * @param homeAccountId
836
- */
837
- matchHomeAccountId(entity, homeAccountId) {
838
- return !!(typeof entity.homeAccountId === "string" &&
839
- homeAccountId === entity.homeAccountId);
840
- }
841
- /**
842
- * helper to match account ids
843
- * @param entity
844
- * @param localAccountId
845
- * @returns
846
- */
847
- matchLocalAccountId(entity, localAccountId) {
848
- return !!(typeof entity.localAccountId === "string" &&
849
- localAccountId === entity.localAccountId);
850
- }
851
- /**
852
- * helper to match usernames
853
- * @param entity
854
- * @param username
855
- * @returns
856
- */
857
- matchUsername(entity, username) {
858
- return !!(typeof entity.username === "string" &&
859
- username.toLowerCase() === entity.username.toLowerCase());
860
- }
861
- /**
862
- * helper to match names
863
- * @param entity
864
- * @param name
865
- * @returns true if the downcased name properties are present and match in the filter and the entity
866
- */
867
- matchName(entity, name) {
868
- return !!(name.toLowerCase() === entity.name?.toLowerCase());
869
- }
870
- /**
871
- * helper to match assertion
872
- * @param value
873
- * @param oboAssertion
874
- */
875
- matchUserAssertionHash(entity, userAssertionHash) {
876
- return !!(entity.userAssertionHash &&
877
- userAssertionHash === entity.userAssertionHash);
878
- }
879
- /**
880
- * helper to match environment
881
- * @param value
882
- * @param environment
883
- */
884
- matchEnvironment(entity, environment) {
885
- // Check static authority options first for cases where authority metadata has not been resolved and cached yet
886
- if (this.staticAuthorityOptions) {
887
- const staticAliases = getAliasesFromStaticSources(this.staticAuthorityOptions, this.commonLogger);
888
- if (staticAliases.includes(environment) &&
889
- staticAliases.includes(entity.environment)) {
890
- return true;
891
- }
892
- }
893
- // Query metadata cache if no static authority configuration has aliases that match enviroment
894
- const cloudMetadata = this.getAuthorityMetadataByAlias(environment);
895
- if (cloudMetadata &&
896
- cloudMetadata.aliases.indexOf(entity.environment) > -1) {
897
- return true;
898
- }
899
- return false;
900
- }
901
- /**
902
- * helper to match credential type
903
- * @param entity
904
- * @param credentialType
905
- */
906
- matchCredentialType(entity, credentialType) {
907
- return (entity.credentialType &&
908
- credentialType.toLowerCase() === entity.credentialType.toLowerCase());
909
- }
910
- /**
911
- * helper to match client ids
912
- * @param entity
913
- * @param clientId
914
- */
915
- matchClientId(entity, clientId) {
916
- return !!(entity.clientId && clientId === entity.clientId);
917
- }
918
- /**
919
- * helper to match family ids
920
- * @param entity
921
- * @param familyId
922
- */
923
- matchFamilyId(entity, familyId) {
924
- return !!(entity.familyId && familyId === entity.familyId);
925
- }
926
- /**
927
- * helper to match realm
928
- * @param entity
929
- * @param realm
930
- */
931
- matchRealm(entity, realm) {
932
- return !!(entity.realm && realm === entity.realm);
933
- }
934
- /**
935
- * helper to match nativeAccountId
936
- * @param entity
937
- * @param nativeAccountId
938
- * @returns boolean indicating the match result
939
- */
940
- matchNativeAccountId(entity, nativeAccountId) {
941
- return !!(entity.nativeAccountId && nativeAccountId === entity.nativeAccountId);
942
- }
943
- /**
944
- * helper to match loginHint which can be either:
945
- * 1. login_hint ID token claim
946
- * 2. username in cached account object
947
- * 3. upn in ID token claims
948
- * @param entity
949
- * @param loginHint
950
- * @returns
951
- */
952
- matchLoginHint(idTokenClaims, loginHint) {
953
- if (idTokenClaims?.login_hint === loginHint) {
954
- return true;
955
- }
956
- if (idTokenClaims.preferred_username === loginHint) {
957
- return true;
958
- }
959
- if (idTokenClaims?.upn === loginHint) {
960
- return true;
961
- }
962
- return false;
963
- }
964
- /**
965
- * Helper to match sid
966
- * @param idTokenClaims
967
- * @param sid
968
- * @returns true if the sid claim is present and matches the filter
969
- */
970
- matchSid(idTokenClaims, sid) {
971
- return !!(idTokenClaims?.sid && idTokenClaims.sid === sid);
972
- }
973
- matchAuthorityType(entity, authorityType) {
974
- return !!(entity.authorityType &&
975
- authorityType.toLowerCase() === entity.authorityType.toLowerCase());
976
- }
977
- /**
978
- * Returns true if the target scopes are a subset of the current entity's scopes, false otherwise.
979
- * @param entity
980
- * @param target
981
- */
982
- matchTarget(entity, target) {
983
- const isNotAccessTokenCredential = entity.credentialType !== CredentialType.ACCESS_TOKEN &&
984
- entity.credentialType !==
985
- CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME;
986
- if (isNotAccessTokenCredential || !entity.target) {
987
- return false;
988
- }
989
- const entityScopeSet = ScopeSet.fromString(entity.target);
990
- return entityScopeSet.containsScopeSet(target);
991
- }
992
- /**
993
- * Returns true if the credential's tokenType or Authentication Scheme matches the one in the request, false otherwise
994
- * @param entity
995
- * @param tokenType
996
- */
997
- matchTokenType(entity, tokenType) {
998
- return !!(entity.tokenType && entity.tokenType === tokenType);
999
- }
1000
- /**
1001
- * Returns true if the credential's keyId matches the one in the request, false otherwise
1002
- * @param entity
1003
- * @param tokenType
1004
- */
1005
- matchKeyId(entity, keyId) {
1006
- return !!(entity.keyId && entity.keyId === keyId);
1007
- }
1008
- /**
1009
- * returns if a given cache entity is of the type appmetadata
1010
- * @param key
1011
- */
1012
- isAppMetadata(key) {
1013
- return key.indexOf(APP_METADATA) !== -1;
1014
- }
1015
- /**
1016
- * returns if a given cache entity is of the type authoritymetadata
1017
- * @param key
1018
- */
1019
- isAuthorityMetadata(key) {
1020
- return key.indexOf(AUTHORITY_METADATA_CONSTANTS.CACHE_KEY) !== -1;
1021
- }
1022
- /**
1023
- * returns cache key used for cloud instance metadata
1024
- */
1025
- generateAuthorityMetadataCacheKey(authority) {
1026
- return `${AUTHORITY_METADATA_CONSTANTS.CACHE_KEY}-${this.clientId}-${authority}`;
1027
- }
1028
- /**
1029
- * Helper to convert serialized data to object
1030
- * @param obj
1031
- * @param json
1032
- */
1033
- static toObject(obj, json) {
1034
- for (const propertyName in json) {
1035
- obj[propertyName] = json[propertyName];
1036
- }
1037
- return obj;
1038
- }
1039
- }
1040
- /** @internal */
1041
- class DefaultStorageClass extends CacheManager {
1042
- setAccount() {
1043
- throw createClientAuthError(methodNotImplemented);
1044
- }
1045
- getAccount() {
1046
- throw createClientAuthError(methodNotImplemented);
1047
- }
1048
- setIdTokenCredential() {
1049
- throw createClientAuthError(methodNotImplemented);
1050
- }
1051
- getIdTokenCredential() {
1052
- throw createClientAuthError(methodNotImplemented);
1053
- }
1054
- setAccessTokenCredential() {
1055
- throw createClientAuthError(methodNotImplemented);
1056
- }
1057
- getAccessTokenCredential() {
1058
- throw createClientAuthError(methodNotImplemented);
1059
- }
1060
- setRefreshTokenCredential() {
1061
- throw createClientAuthError(methodNotImplemented);
1062
- }
1063
- getRefreshTokenCredential() {
1064
- throw createClientAuthError(methodNotImplemented);
1065
- }
1066
- setAppMetadata() {
1067
- throw createClientAuthError(methodNotImplemented);
1068
- }
1069
- getAppMetadata() {
1070
- throw createClientAuthError(methodNotImplemented);
1071
- }
1072
- setServerTelemetry() {
1073
- throw createClientAuthError(methodNotImplemented);
1074
- }
1075
- getServerTelemetry() {
1076
- throw createClientAuthError(methodNotImplemented);
1077
- }
1078
- setAuthorityMetadata() {
1079
- throw createClientAuthError(methodNotImplemented);
1080
- }
1081
- getAuthorityMetadata() {
1082
- throw createClientAuthError(methodNotImplemented);
1083
- }
1084
- getAuthorityMetadataKeys() {
1085
- throw createClientAuthError(methodNotImplemented);
1086
- }
1087
- setThrottlingCache() {
1088
- throw createClientAuthError(methodNotImplemented);
1089
- }
1090
- getThrottlingCache() {
1091
- throw createClientAuthError(methodNotImplemented);
1092
- }
1093
- removeItem() {
1094
- throw createClientAuthError(methodNotImplemented);
1095
- }
1096
- containsKey() {
1097
- throw createClientAuthError(methodNotImplemented);
1098
- }
1099
- getKeys() {
1100
- throw createClientAuthError(methodNotImplemented);
1101
- }
1102
- getAccountKeys() {
1103
- throw createClientAuthError(methodNotImplemented);
1104
- }
1105
- getTokenKeys() {
1106
- throw createClientAuthError(methodNotImplemented);
1107
- }
1108
- async clear() {
1109
- throw createClientAuthError(methodNotImplemented);
1110
- }
1111
- updateCredentialCacheKey() {
1112
- throw createClientAuthError(methodNotImplemented);
1113
- }
15
+ /*
16
+ * Copyright (c) Microsoft Corporation. All rights reserved.
17
+ * Licensed under the MIT License.
18
+ */
19
+ /**
20
+ * Interface class which implement cache storage functions used by MSAL to perform validity checks, and store tokens.
21
+ * @internal
22
+ */
23
+ class CacheManager {
24
+ constructor(clientId, cryptoImpl, logger, staticAuthorityOptions) {
25
+ this.clientId = clientId;
26
+ this.cryptoImpl = cryptoImpl;
27
+ this.commonLogger = logger.clone(name, version);
28
+ this.staticAuthorityOptions = staticAuthorityOptions;
29
+ }
30
+ /**
31
+ * Returns all the accounts in the cache that match the optional filter. If no filter is provided, all accounts are returned.
32
+ * @param accountFilter - (Optional) filter to narrow down the accounts returned
33
+ * @returns Array of AccountInfo objects in cache
34
+ */
35
+ getAllAccounts(accountFilter) {
36
+ return this.buildTenantProfiles(this.getAccountsFilteredBy(accountFilter || {}), accountFilter);
37
+ }
38
+ /**
39
+ * Gets first tenanted AccountInfo object found based on provided filters
40
+ */
41
+ getAccountInfoFilteredBy(accountFilter) {
42
+ const allAccounts = this.getAllAccounts(accountFilter);
43
+ if (allAccounts.length > 1) {
44
+ // If one or more accounts are found, prioritize accounts that have an ID token
45
+ const sortedAccounts = allAccounts.sort((account) => {
46
+ return account.idTokenClaims ? -1 : 1;
47
+ });
48
+ return sortedAccounts[0];
49
+ }
50
+ else if (allAccounts.length === 1) {
51
+ // If only one account is found, return it regardless of whether a matching ID token was found
52
+ return allAccounts[0];
53
+ }
54
+ else {
55
+ return null;
56
+ }
57
+ }
58
+ /**
59
+ * Returns a single matching
60
+ * @param accountFilter
61
+ * @returns
62
+ */
63
+ getBaseAccountInfo(accountFilter) {
64
+ const accountEntities = this.getAccountsFilteredBy(accountFilter);
65
+ if (accountEntities.length > 0) {
66
+ return accountEntities[0].getAccountInfo();
67
+ }
68
+ else {
69
+ return null;
70
+ }
71
+ }
72
+ /**
73
+ * Matches filtered account entities with cached ID tokens that match the tenant profile-specific account filters
74
+ * and builds the account info objects from the matching ID token's claims
75
+ * @param cachedAccounts
76
+ * @param accountFilter
77
+ * @returns Array of AccountInfo objects that match account and tenant profile filters
78
+ */
79
+ buildTenantProfiles(cachedAccounts, accountFilter) {
80
+ return cachedAccounts.flatMap((accountEntity) => {
81
+ return this.getAccountInfoForTenantProfiles(accountEntity, accountFilter);
82
+ });
83
+ }
84
+ getAccountInfoForTenantProfiles(accountEntity, accountFilter) {
85
+ return this.getTenantProfilesFromAccountEntity(accountEntity, accountFilter?.tenantId, accountFilter);
86
+ }
87
+ getTenantedAccountInfoByFilter(accountInfo, tokenKeys, tenantProfile, tenantProfileFilter) {
88
+ let tenantedAccountInfo = null;
89
+ let idTokenClaims;
90
+ if (tenantProfileFilter) {
91
+ if (!this.tenantProfileMatchesFilter(tenantProfile, tenantProfileFilter)) {
92
+ return null;
93
+ }
94
+ }
95
+ const idToken = this.getIdToken(accountInfo, tokenKeys, tenantProfile.tenantId);
96
+ if (idToken) {
97
+ idTokenClaims = extractTokenClaims(idToken.secret, this.cryptoImpl.base64Decode);
98
+ if (!this.idTokenClaimsMatchTenantProfileFilter(idTokenClaims, tenantProfileFilter)) {
99
+ // ID token sourced claims don't match so this tenant profile is not a match
100
+ return null;
101
+ }
102
+ }
103
+ // Expand tenant profile into account info based on matching tenant profile and if available matching ID token claims
104
+ tenantedAccountInfo = updateAccountTenantProfileData(accountInfo, tenantProfile, idTokenClaims);
105
+ return tenantedAccountInfo;
106
+ }
107
+ getTenantProfilesFromAccountEntity(accountEntity, targetTenantId, tenantProfileFilter) {
108
+ const accountInfo = accountEntity.getAccountInfo();
109
+ let searchTenantProfiles = accountInfo.tenantProfiles || new Map();
110
+ const tokenKeys = this.getTokenKeys();
111
+ // If a tenant ID was provided, only return the tenant profile for that tenant ID if it exists
112
+ if (targetTenantId) {
113
+ const tenantProfile = searchTenantProfiles.get(targetTenantId);
114
+ if (tenantProfile) {
115
+ // Reduce search field to just this tenant profile
116
+ searchTenantProfiles = new Map([
117
+ [targetTenantId, tenantProfile],
118
+ ]);
119
+ }
120
+ else {
121
+ // No tenant profile for search tenant ID, return empty array
122
+ return [];
123
+ }
124
+ }
125
+ const matchingTenantProfiles = [];
126
+ searchTenantProfiles.forEach((tenantProfile) => {
127
+ const tenantedAccountInfo = this.getTenantedAccountInfoByFilter(accountInfo, tokenKeys, tenantProfile, tenantProfileFilter);
128
+ if (tenantedAccountInfo) {
129
+ matchingTenantProfiles.push(tenantedAccountInfo);
130
+ }
131
+ });
132
+ return matchingTenantProfiles;
133
+ }
134
+ tenantProfileMatchesFilter(tenantProfile, tenantProfileFilter) {
135
+ if (!!tenantProfileFilter.localAccountId &&
136
+ !this.matchLocalAccountIdFromTenantProfile(tenantProfile, tenantProfileFilter.localAccountId)) {
137
+ return false;
138
+ }
139
+ if (!!tenantProfileFilter.name &&
140
+ !(tenantProfile.name === tenantProfileFilter.name)) {
141
+ return false;
142
+ }
143
+ if (tenantProfileFilter.isHomeTenant !== undefined &&
144
+ !(tenantProfile.isHomeTenant === tenantProfileFilter.isHomeTenant)) {
145
+ return false;
146
+ }
147
+ return true;
148
+ }
149
+ idTokenClaimsMatchTenantProfileFilter(idTokenClaims, tenantProfileFilter) {
150
+ // Tenant Profile filtering
151
+ if (tenantProfileFilter) {
152
+ if (!!tenantProfileFilter.localAccountId &&
153
+ !this.matchLocalAccountIdFromTokenClaims(idTokenClaims, tenantProfileFilter.localAccountId)) {
154
+ return false;
155
+ }
156
+ if (!!tenantProfileFilter.loginHint &&
157
+ !this.matchLoginHintFromTokenClaims(idTokenClaims, tenantProfileFilter.loginHint)) {
158
+ return false;
159
+ }
160
+ if (!!tenantProfileFilter.username &&
161
+ !this.matchUsername(idTokenClaims.preferred_username, tenantProfileFilter.username)) {
162
+ return false;
163
+ }
164
+ if (!!tenantProfileFilter.name &&
165
+ !this.matchName(idTokenClaims, tenantProfileFilter.name)) {
166
+ return false;
167
+ }
168
+ if (!!tenantProfileFilter.sid &&
169
+ !this.matchSid(idTokenClaims, tenantProfileFilter.sid)) {
170
+ return false;
171
+ }
172
+ }
173
+ return true;
174
+ }
175
+ /**
176
+ * saves a cache record
177
+ * @param cacheRecord
178
+ */
179
+ async saveCacheRecord(cacheRecord, storeInCache) {
180
+ if (!cacheRecord) {
181
+ throw createClientAuthError(invalidCacheRecord);
182
+ }
183
+ if (!!cacheRecord.account) {
184
+ this.setAccount(cacheRecord.account);
185
+ }
186
+ if (!!cacheRecord.idToken && storeInCache?.idToken !== false) {
187
+ this.setIdTokenCredential(cacheRecord.idToken);
188
+ }
189
+ if (!!cacheRecord.accessToken && storeInCache?.accessToken !== false) {
190
+ await this.saveAccessToken(cacheRecord.accessToken);
191
+ }
192
+ if (!!cacheRecord.refreshToken &&
193
+ storeInCache?.refreshToken !== false) {
194
+ this.setRefreshTokenCredential(cacheRecord.refreshToken);
195
+ }
196
+ if (!!cacheRecord.appMetadata) {
197
+ this.setAppMetadata(cacheRecord.appMetadata);
198
+ }
199
+ }
200
+ /**
201
+ * saves access token credential
202
+ * @param credential
203
+ */
204
+ async saveAccessToken(credential) {
205
+ const accessTokenFilter = {
206
+ clientId: credential.clientId,
207
+ credentialType: credential.credentialType,
208
+ environment: credential.environment,
209
+ homeAccountId: credential.homeAccountId,
210
+ realm: credential.realm,
211
+ tokenType: credential.tokenType,
212
+ requestedClaimsHash: credential.requestedClaimsHash,
213
+ };
214
+ const tokenKeys = this.getTokenKeys();
215
+ const currentScopes = ScopeSet.fromString(credential.target);
216
+ const removedAccessTokens = [];
217
+ tokenKeys.accessToken.forEach((key) => {
218
+ if (!this.accessTokenKeyMatchesFilter(key, accessTokenFilter, false)) {
219
+ return;
220
+ }
221
+ const tokenEntity = this.getAccessTokenCredential(key);
222
+ if (tokenEntity &&
223
+ this.credentialMatchesFilter(tokenEntity, accessTokenFilter)) {
224
+ const tokenScopeSet = ScopeSet.fromString(tokenEntity.target);
225
+ if (tokenScopeSet.intersectingScopeSets(currentScopes)) {
226
+ removedAccessTokens.push(this.removeAccessToken(key));
227
+ }
228
+ }
229
+ });
230
+ await Promise.all(removedAccessTokens);
231
+ this.setAccessTokenCredential(credential);
232
+ }
233
+ /**
234
+ * Retrieve account entities matching all provided tenant-agnostic filters; if no filter is set, get all account entities in the cache
235
+ * Not checking for casing as keys are all generated in lower case, remember to convert to lower case if object properties are compared
236
+ * @param accountFilter - An object containing Account properties to filter by
237
+ */
238
+ getAccountsFilteredBy(accountFilter) {
239
+ const allAccountKeys = this.getAccountKeys();
240
+ const matchingAccounts = [];
241
+ allAccountKeys.forEach((cacheKey) => {
242
+ if (!this.isAccountKey(cacheKey, accountFilter.homeAccountId)) {
243
+ // Don't parse value if the key doesn't match the account filters
244
+ return;
245
+ }
246
+ const entity = this.getAccount(cacheKey, this.commonLogger);
247
+ // Match base account fields
248
+ if (!entity) {
249
+ return;
250
+ }
251
+ if (!!accountFilter.homeAccountId &&
252
+ !this.matchHomeAccountId(entity, accountFilter.homeAccountId)) {
253
+ return;
254
+ }
255
+ if (!!accountFilter.username &&
256
+ !this.matchUsername(entity.username, accountFilter.username)) {
257
+ return;
258
+ }
259
+ if (!!accountFilter.environment &&
260
+ !this.matchEnvironment(entity, accountFilter.environment)) {
261
+ return;
262
+ }
263
+ if (!!accountFilter.realm &&
264
+ !this.matchRealm(entity, accountFilter.realm)) {
265
+ return;
266
+ }
267
+ if (!!accountFilter.nativeAccountId &&
268
+ !this.matchNativeAccountId(entity, accountFilter.nativeAccountId)) {
269
+ return;
270
+ }
271
+ if (!!accountFilter.authorityType &&
272
+ !this.matchAuthorityType(entity, accountFilter.authorityType)) {
273
+ return;
274
+ }
275
+ // If at least one tenant profile matches the tenant profile filter, add the account to the list of matching accounts
276
+ const tenantProfileFilter = {
277
+ localAccountId: accountFilter?.localAccountId,
278
+ name: accountFilter?.name,
279
+ };
280
+ const matchingTenantProfiles = entity.tenantProfiles?.filter((tenantProfile) => {
281
+ return this.tenantProfileMatchesFilter(tenantProfile, tenantProfileFilter);
282
+ });
283
+ if (matchingTenantProfiles && matchingTenantProfiles.length === 0) {
284
+ // No tenant profile for this account matches filter, don't add to list of matching accounts
285
+ return;
286
+ }
287
+ matchingAccounts.push(entity);
288
+ });
289
+ return matchingAccounts;
290
+ }
291
+ /**
292
+ * Returns true if the given key matches our account key schema. Also matches homeAccountId and/or tenantId if provided
293
+ * @param key
294
+ * @param homeAccountId
295
+ * @param tenantId
296
+ * @returns
297
+ */
298
+ isAccountKey(key, homeAccountId, tenantId) {
299
+ if (key.split(Separators.CACHE_KEY_SEPARATOR).length < 3) {
300
+ // Account cache keys contain 3 items separated by '-' (each item may also contain '-')
301
+ return false;
302
+ }
303
+ if (homeAccountId &&
304
+ !key.toLowerCase().includes(homeAccountId.toLowerCase())) {
305
+ return false;
306
+ }
307
+ if (tenantId && !key.toLowerCase().includes(tenantId.toLowerCase())) {
308
+ return false;
309
+ }
310
+ // Do not check environment as aliasing can cause false negatives
311
+ return true;
312
+ }
313
+ /**
314
+ * Returns true if the given key matches our credential key schema.
315
+ * @param key
316
+ */
317
+ isCredentialKey(key) {
318
+ if (key.split(Separators.CACHE_KEY_SEPARATOR).length < 6) {
319
+ // Credential cache keys contain 6 items separated by '-' (each item may also contain '-')
320
+ return false;
321
+ }
322
+ const lowerCaseKey = key.toLowerCase();
323
+ // Credential keys must indicate what credential type they represent
324
+ if (lowerCaseKey.indexOf(CredentialType.ID_TOKEN.toLowerCase()) ===
325
+ -1 &&
326
+ lowerCaseKey.indexOf(CredentialType.ACCESS_TOKEN.toLowerCase()) ===
327
+ -1 &&
328
+ lowerCaseKey.indexOf(CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME.toLowerCase()) === -1 &&
329
+ lowerCaseKey.indexOf(CredentialType.REFRESH_TOKEN.toLowerCase()) ===
330
+ -1) {
331
+ return false;
332
+ }
333
+ if (lowerCaseKey.indexOf(CredentialType.REFRESH_TOKEN.toLowerCase()) >
334
+ -1) {
335
+ // Refresh tokens must contain the client id or family id
336
+ const clientIdValidation = `${CredentialType.REFRESH_TOKEN}${Separators.CACHE_KEY_SEPARATOR}${this.clientId}${Separators.CACHE_KEY_SEPARATOR}`;
337
+ const familyIdValidation = `${CredentialType.REFRESH_TOKEN}${Separators.CACHE_KEY_SEPARATOR}${THE_FAMILY_ID}${Separators.CACHE_KEY_SEPARATOR}`;
338
+ if (lowerCaseKey.indexOf(clientIdValidation.toLowerCase()) === -1 &&
339
+ lowerCaseKey.indexOf(familyIdValidation.toLowerCase()) === -1) {
340
+ return false;
341
+ }
342
+ }
343
+ else if (lowerCaseKey.indexOf(this.clientId.toLowerCase()) === -1) {
344
+ // Tokens must contain the clientId
345
+ return false;
346
+ }
347
+ return true;
348
+ }
349
+ /**
350
+ * Returns whether or not the given credential entity matches the filter
351
+ * @param entity
352
+ * @param filter
353
+ * @returns
354
+ */
355
+ credentialMatchesFilter(entity, filter) {
356
+ if (!!filter.clientId && !this.matchClientId(entity, filter.clientId)) {
357
+ return false;
358
+ }
359
+ if (!!filter.userAssertionHash &&
360
+ !this.matchUserAssertionHash(entity, filter.userAssertionHash)) {
361
+ return false;
362
+ }
363
+ /*
364
+ * homeAccountId can be undefined, and we want to filter out cached items that have a homeAccountId of ""
365
+ * because we don't want a client_credential request to return a cached token that has a homeAccountId
366
+ */
367
+ if (typeof filter.homeAccountId === "string" &&
368
+ !this.matchHomeAccountId(entity, filter.homeAccountId)) {
369
+ return false;
370
+ }
371
+ if (!!filter.environment &&
372
+ !this.matchEnvironment(entity, filter.environment)) {
373
+ return false;
374
+ }
375
+ if (!!filter.realm && !this.matchRealm(entity, filter.realm)) {
376
+ return false;
377
+ }
378
+ if (!!filter.credentialType &&
379
+ !this.matchCredentialType(entity, filter.credentialType)) {
380
+ return false;
381
+ }
382
+ if (!!filter.familyId && !this.matchFamilyId(entity, filter.familyId)) {
383
+ return false;
384
+ }
385
+ /*
386
+ * idTokens do not have "target", target specific refreshTokens do exist for some types of authentication
387
+ * Resource specific refresh tokens case will be added when the support is deemed necessary
388
+ */
389
+ if (!!filter.target && !this.matchTarget(entity, filter.target)) {
390
+ return false;
391
+ }
392
+ // If request OR cached entity has requested Claims Hash, check if they match
393
+ if (filter.requestedClaimsHash || entity.requestedClaimsHash) {
394
+ // Don't match if either is undefined or they are different
395
+ if (entity.requestedClaimsHash !== filter.requestedClaimsHash) {
396
+ return false;
397
+ }
398
+ }
399
+ // Access Token with Auth Scheme specific matching
400
+ if (entity.credentialType ===
401
+ CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME) {
402
+ if (!!filter.tokenType &&
403
+ !this.matchTokenType(entity, filter.tokenType)) {
404
+ return false;
405
+ }
406
+ // KeyId (sshKid) in request must match cached SSH certificate keyId because SSH cert is bound to a specific key
407
+ if (filter.tokenType === AuthenticationScheme.SSH) {
408
+ if (filter.keyId && !this.matchKeyId(entity, filter.keyId)) {
409
+ return false;
410
+ }
411
+ }
412
+ }
413
+ return true;
414
+ }
415
+ /**
416
+ * retrieve appMetadata matching all provided filters; if no filter is set, get all appMetadata
417
+ * @param filter
418
+ */
419
+ getAppMetadataFilteredBy(filter) {
420
+ return this.getAppMetadataFilteredByInternal(filter.environment, filter.clientId);
421
+ }
422
+ /**
423
+ * Support function to help match appMetadata
424
+ * @param environment
425
+ * @param clientId
426
+ */
427
+ getAppMetadataFilteredByInternal(environment, clientId) {
428
+ const allCacheKeys = this.getKeys();
429
+ const matchingAppMetadata = {};
430
+ allCacheKeys.forEach((cacheKey) => {
431
+ // don't parse any non-appMetadata type cache entities
432
+ if (!this.isAppMetadata(cacheKey)) {
433
+ return;
434
+ }
435
+ // Attempt retrieval
436
+ const entity = this.getAppMetadata(cacheKey);
437
+ if (!entity) {
438
+ return;
439
+ }
440
+ if (!!environment && !this.matchEnvironment(entity, environment)) {
441
+ return;
442
+ }
443
+ if (!!clientId && !this.matchClientId(entity, clientId)) {
444
+ return;
445
+ }
446
+ matchingAppMetadata[cacheKey] = entity;
447
+ });
448
+ return matchingAppMetadata;
449
+ }
450
+ /**
451
+ * retrieve authorityMetadata that contains a matching alias
452
+ * @param filter
453
+ */
454
+ getAuthorityMetadataByAlias(host) {
455
+ const allCacheKeys = this.getAuthorityMetadataKeys();
456
+ let matchedEntity = null;
457
+ allCacheKeys.forEach((cacheKey) => {
458
+ // don't parse any non-authorityMetadata type cache entities
459
+ if (!this.isAuthorityMetadata(cacheKey) ||
460
+ cacheKey.indexOf(this.clientId) === -1) {
461
+ return;
462
+ }
463
+ // Attempt retrieval
464
+ const entity = this.getAuthorityMetadata(cacheKey);
465
+ if (!entity) {
466
+ return;
467
+ }
468
+ if (entity.aliases.indexOf(host) === -1) {
469
+ return;
470
+ }
471
+ matchedEntity = entity;
472
+ });
473
+ return matchedEntity;
474
+ }
475
+ /**
476
+ * Removes all accounts and related tokens from cache.
477
+ */
478
+ async removeAllAccounts() {
479
+ const allAccountKeys = this.getAccountKeys();
480
+ const removedAccounts = [];
481
+ allAccountKeys.forEach((cacheKey) => {
482
+ removedAccounts.push(this.removeAccount(cacheKey));
483
+ });
484
+ await Promise.all(removedAccounts);
485
+ }
486
+ /**
487
+ * Removes the account and related tokens for a given account key
488
+ * @param account
489
+ */
490
+ async removeAccount(accountKey) {
491
+ const account = this.getAccount(accountKey, this.commonLogger);
492
+ if (!account) {
493
+ return;
494
+ }
495
+ await this.removeAccountContext(account);
496
+ this.removeItem(accountKey);
497
+ }
498
+ /**
499
+ * Removes credentials associated with the provided account
500
+ * @param account
501
+ */
502
+ async removeAccountContext(account) {
503
+ const allTokenKeys = this.getTokenKeys();
504
+ const accountId = account.generateAccountId();
505
+ const removedCredentials = [];
506
+ allTokenKeys.idToken.forEach((key) => {
507
+ if (key.indexOf(accountId) === 0) {
508
+ this.removeIdToken(key);
509
+ }
510
+ });
511
+ allTokenKeys.accessToken.forEach((key) => {
512
+ if (key.indexOf(accountId) === 0) {
513
+ removedCredentials.push(this.removeAccessToken(key));
514
+ }
515
+ });
516
+ allTokenKeys.refreshToken.forEach((key) => {
517
+ if (key.indexOf(accountId) === 0) {
518
+ this.removeRefreshToken(key);
519
+ }
520
+ });
521
+ await Promise.all(removedCredentials);
522
+ }
523
+ /**
524
+ * Migrates a single-tenant account and all it's associated alternate cross-tenant account objects in the
525
+ * cache into a condensed multi-tenant account object with tenant profiles.
526
+ * @param accountKey
527
+ * @param accountEntity
528
+ * @param logger
529
+ * @returns
530
+ */
531
+ updateOutdatedCachedAccount(accountKey, accountEntity, logger) {
532
+ // Only update if account entity is defined and has no tenantProfiles object (is outdated)
533
+ if (accountEntity && accountEntity.isSingleTenant()) {
534
+ this.commonLogger?.verbose("updateOutdatedCachedAccount: Found a single-tenant (outdated) account entity in the cache, migrating to multi-tenant account entity");
535
+ // Get keys of all accounts belonging to user
536
+ const matchingAccountKeys = this.getAccountKeys().filter((key) => {
537
+ return key.startsWith(accountEntity.homeAccountId);
538
+ });
539
+ // Get all account entities belonging to user
540
+ const accountsToMerge = [];
541
+ matchingAccountKeys.forEach((key) => {
542
+ const account = this.getCachedAccountEntity(key);
543
+ if (account) {
544
+ accountsToMerge.push(account);
545
+ }
546
+ });
547
+ // Set base account to home account if available, any account if not
548
+ const baseAccount = accountsToMerge.find((account) => {
549
+ return tenantIdMatchesHomeTenant(account.realm, account.homeAccountId);
550
+ }) || accountsToMerge[0];
551
+ // Populate tenant profiles built from each account entity belonging to the user
552
+ baseAccount.tenantProfiles = accountsToMerge.map((account) => {
553
+ return {
554
+ tenantId: account.realm,
555
+ localAccountId: account.localAccountId,
556
+ name: account.name,
557
+ isHomeTenant: tenantIdMatchesHomeTenant(account.realm, account.homeAccountId),
558
+ };
559
+ });
560
+ const updatedAccount = CacheManager.toObject(new AccountEntity(), {
561
+ ...baseAccount,
562
+ });
563
+ const newAccountKey = updatedAccount.generateAccountKey();
564
+ // Clear cache of legacy account objects that have been collpsed into tenant profiles
565
+ matchingAccountKeys.forEach((key) => {
566
+ if (key !== newAccountKey) {
567
+ this.removeOutdatedAccount(accountKey);
568
+ }
569
+ });
570
+ // Cache updated account object
571
+ this.setAccount(updatedAccount);
572
+ logger?.verbose("Updated an outdated account entity in the cache");
573
+ return updatedAccount;
574
+ }
575
+ // No update is necessary
576
+ return accountEntity;
577
+ }
578
+ /**
579
+ * returns a boolean if the given credential is removed
580
+ * @param credential
581
+ */
582
+ async removeAccessToken(key) {
583
+ const credential = this.getAccessTokenCredential(key);
584
+ if (!credential) {
585
+ return;
586
+ }
587
+ // Remove Token Binding Key from key store for PoP Tokens Credentials
588
+ if (credential.credentialType.toLowerCase() ===
589
+ CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME.toLowerCase()) {
590
+ if (credential.tokenType === AuthenticationScheme.POP) {
591
+ const accessTokenWithAuthSchemeEntity = credential;
592
+ const kid = accessTokenWithAuthSchemeEntity.keyId;
593
+ if (kid) {
594
+ try {
595
+ await this.cryptoImpl.removeTokenBindingKey(kid);
596
+ }
597
+ catch (error) {
598
+ throw createClientAuthError(bindingKeyNotRemoved);
599
+ }
600
+ }
601
+ }
602
+ }
603
+ return this.removeItem(key);
604
+ }
605
+ /**
606
+ * Removes all app metadata objects from cache.
607
+ */
608
+ removeAppMetadata() {
609
+ const allCacheKeys = this.getKeys();
610
+ allCacheKeys.forEach((cacheKey) => {
611
+ if (this.isAppMetadata(cacheKey)) {
612
+ this.removeItem(cacheKey);
613
+ }
614
+ });
615
+ return true;
616
+ }
617
+ /**
618
+ * Retrieve the cached credentials into a cacherecord
619
+ * @param account {AccountInfo}
620
+ * @param request {BaseAuthRequest}
621
+ * @param environment {string}
622
+ * @param performanceClient {?IPerformanceClient}
623
+ * @param correlationId {?string}
624
+ */
625
+ readCacheRecord(account, request, environment, performanceClient, correlationId) {
626
+ // Use authority tenantId for cache lookup filter if it's defined, otherwise use tenantId from account passed in
627
+ const requestTenantId = account.tenantId || getTenantFromAuthorityString(request.authority);
628
+ const tokenKeys = this.getTokenKeys();
629
+ const cachedAccount = this.readAccountFromCache(account);
630
+ const cachedIdToken = this.getIdToken(account, tokenKeys, requestTenantId, performanceClient, correlationId);
631
+ const cachedAccessToken = this.getAccessToken(account, request, tokenKeys, requestTenantId, performanceClient, correlationId);
632
+ const cachedRefreshToken = this.getRefreshToken(account, false, tokenKeys, performanceClient, correlationId);
633
+ const cachedAppMetadata = this.readAppMetadataFromCache(environment);
634
+ return {
635
+ account: cachedAccount,
636
+ idToken: cachedIdToken,
637
+ accessToken: cachedAccessToken,
638
+ refreshToken: cachedRefreshToken,
639
+ appMetadata: cachedAppMetadata,
640
+ };
641
+ }
642
+ /**
643
+ * Retrieve AccountEntity from cache
644
+ * @param account
645
+ */
646
+ readAccountFromCache(account) {
647
+ const accountKey = AccountEntity.generateAccountCacheKey(account);
648
+ return this.getAccount(accountKey, this.commonLogger);
649
+ }
650
+ /**
651
+ * Retrieve IdTokenEntity from cache
652
+ * @param account {AccountInfo}
653
+ * @param tokenKeys {?TokenKeys}
654
+ * @param targetRealm {?string}
655
+ * @param performanceClient {?IPerformanceClient}
656
+ * @param correlationId {?string}
657
+ */
658
+ getIdToken(account, tokenKeys, targetRealm, performanceClient, correlationId) {
659
+ this.commonLogger.trace("CacheManager - getIdToken called");
660
+ const idTokenFilter = {
661
+ homeAccountId: account.homeAccountId,
662
+ environment: account.environment,
663
+ credentialType: CredentialType.ID_TOKEN,
664
+ clientId: this.clientId,
665
+ realm: targetRealm,
666
+ };
667
+ const idTokenMap = this.getIdTokensByFilter(idTokenFilter, tokenKeys);
668
+ const numIdTokens = idTokenMap.size;
669
+ if (numIdTokens < 1) {
670
+ this.commonLogger.info("CacheManager:getIdToken - No token found");
671
+ return null;
672
+ }
673
+ else if (numIdTokens > 1) {
674
+ let tokensToBeRemoved = idTokenMap;
675
+ // Multiple tenant profiles and no tenant specified, pick home account
676
+ if (!targetRealm) {
677
+ const homeIdTokenMap = new Map();
678
+ idTokenMap.forEach((idToken, key) => {
679
+ if (idToken.realm === account.tenantId) {
680
+ homeIdTokenMap.set(key, idToken);
681
+ }
682
+ });
683
+ const numHomeIdTokens = homeIdTokenMap.size;
684
+ if (numHomeIdTokens < 1) {
685
+ this.commonLogger.info("CacheManager:getIdToken - Multiple ID tokens found for account but none match account entity tenant id, returning first result");
686
+ return idTokenMap.values().next().value;
687
+ }
688
+ else if (numHomeIdTokens === 1) {
689
+ this.commonLogger.info("CacheManager:getIdToken - Multiple ID tokens found for account, defaulting to home tenant profile");
690
+ return homeIdTokenMap.values().next().value;
691
+ }
692
+ else {
693
+ // Multiple ID tokens for home tenant profile, remove all and return null
694
+ tokensToBeRemoved = homeIdTokenMap;
695
+ }
696
+ }
697
+ // Multiple tokens for a single tenant profile, remove all and return null
698
+ this.commonLogger.info("CacheManager:getIdToken - Multiple matching ID tokens found, clearing them");
699
+ tokensToBeRemoved.forEach((idToken, key) => {
700
+ this.removeIdToken(key);
701
+ });
702
+ if (performanceClient && correlationId) {
703
+ performanceClient.addFields({ multiMatchedID: idTokenMap.size }, correlationId);
704
+ }
705
+ return null;
706
+ }
707
+ this.commonLogger.info("CacheManager:getIdToken - Returning ID token");
708
+ return idTokenMap.values().next().value;
709
+ }
710
+ /**
711
+ * Gets all idTokens matching the given filter
712
+ * @param filter
713
+ * @returns
714
+ */
715
+ getIdTokensByFilter(filter, tokenKeys) {
716
+ const idTokenKeys = (tokenKeys && tokenKeys.idToken) || this.getTokenKeys().idToken;
717
+ const idTokens = new Map();
718
+ idTokenKeys.forEach((key) => {
719
+ if (!this.idTokenKeyMatchesFilter(key, {
720
+ clientId: this.clientId,
721
+ ...filter,
722
+ })) {
723
+ return;
724
+ }
725
+ const idToken = this.getIdTokenCredential(key);
726
+ if (idToken && this.credentialMatchesFilter(idToken, filter)) {
727
+ idTokens.set(key, idToken);
728
+ }
729
+ });
730
+ return idTokens;
731
+ }
732
+ /**
733
+ * Validate the cache key against filter before retrieving and parsing cache value
734
+ * @param key
735
+ * @param filter
736
+ * @returns
737
+ */
738
+ idTokenKeyMatchesFilter(inputKey, filter) {
739
+ const key = inputKey.toLowerCase();
740
+ if (filter.clientId &&
741
+ key.indexOf(filter.clientId.toLowerCase()) === -1) {
742
+ return false;
743
+ }
744
+ if (filter.homeAccountId &&
745
+ key.indexOf(filter.homeAccountId.toLowerCase()) === -1) {
746
+ return false;
747
+ }
748
+ return true;
749
+ }
750
+ /**
751
+ * Removes idToken from the cache
752
+ * @param key
753
+ */
754
+ removeIdToken(key) {
755
+ this.removeItem(key);
756
+ }
757
+ /**
758
+ * Removes refresh token from the cache
759
+ * @param key
760
+ */
761
+ removeRefreshToken(key) {
762
+ this.removeItem(key);
763
+ }
764
+ /**
765
+ * Retrieve AccessTokenEntity from cache
766
+ * @param account {AccountInfo}
767
+ * @param request {BaseAuthRequest}
768
+ * @param tokenKeys {?TokenKeys}
769
+ * @param performanceClient {?IPerformanceClient}
770
+ * @param correlationId {?string}
771
+ */
772
+ getAccessToken(account, request, tokenKeys, targetRealm, performanceClient, correlationId) {
773
+ this.commonLogger.trace("CacheManager - getAccessToken called");
774
+ const scopes = ScopeSet.createSearchScopes(request.scopes);
775
+ const authScheme = request.authenticationScheme || AuthenticationScheme.BEARER;
776
+ /*
777
+ * Distinguish between Bearer and PoP/SSH token cache types
778
+ * Cast to lowercase to handle "bearer" from ADFS
779
+ */
780
+ const credentialType = authScheme &&
781
+ authScheme.toLowerCase() !==
782
+ AuthenticationScheme.BEARER.toLowerCase()
783
+ ? CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME
784
+ : CredentialType.ACCESS_TOKEN;
785
+ const accessTokenFilter = {
786
+ homeAccountId: account.homeAccountId,
787
+ environment: account.environment,
788
+ credentialType: credentialType,
789
+ clientId: this.clientId,
790
+ realm: targetRealm || account.tenantId,
791
+ target: scopes,
792
+ tokenType: authScheme,
793
+ keyId: request.sshKid,
794
+ requestedClaimsHash: request.requestedClaimsHash,
795
+ };
796
+ const accessTokenKeys = (tokenKeys && tokenKeys.accessToken) ||
797
+ this.getTokenKeys().accessToken;
798
+ const accessTokens = [];
799
+ accessTokenKeys.forEach((key) => {
800
+ // Validate key
801
+ if (this.accessTokenKeyMatchesFilter(key, accessTokenFilter, true)) {
802
+ const accessToken = this.getAccessTokenCredential(key);
803
+ // Validate value
804
+ if (accessToken &&
805
+ this.credentialMatchesFilter(accessToken, accessTokenFilter)) {
806
+ accessTokens.push(accessToken);
807
+ }
808
+ }
809
+ });
810
+ const numAccessTokens = accessTokens.length;
811
+ if (numAccessTokens < 1) {
812
+ this.commonLogger.info("CacheManager:getAccessToken - No token found");
813
+ return null;
814
+ }
815
+ else if (numAccessTokens > 1) {
816
+ this.commonLogger.info("CacheManager:getAccessToken - Multiple access tokens found, clearing them");
817
+ accessTokens.forEach((accessToken) => {
818
+ void this.removeAccessToken(generateCredentialKey(accessToken));
819
+ });
820
+ if (performanceClient && correlationId) {
821
+ performanceClient.addFields({ multiMatchedAT: accessTokens.length }, correlationId);
822
+ }
823
+ return null;
824
+ }
825
+ this.commonLogger.info("CacheManager:getAccessToken - Returning access token");
826
+ return accessTokens[0];
827
+ }
828
+ /**
829
+ * Validate the cache key against filter before retrieving and parsing cache value
830
+ * @param key
831
+ * @param filter
832
+ * @param keyMustContainAllScopes
833
+ * @returns
834
+ */
835
+ accessTokenKeyMatchesFilter(inputKey, filter, keyMustContainAllScopes) {
836
+ const key = inputKey.toLowerCase();
837
+ if (filter.clientId &&
838
+ key.indexOf(filter.clientId.toLowerCase()) === -1) {
839
+ return false;
840
+ }
841
+ if (filter.homeAccountId &&
842
+ key.indexOf(filter.homeAccountId.toLowerCase()) === -1) {
843
+ return false;
844
+ }
845
+ if (filter.realm && key.indexOf(filter.realm.toLowerCase()) === -1) {
846
+ return false;
847
+ }
848
+ if (filter.requestedClaimsHash &&
849
+ key.indexOf(filter.requestedClaimsHash.toLowerCase()) === -1) {
850
+ return false;
851
+ }
852
+ if (filter.target) {
853
+ const scopes = filter.target.asArray();
854
+ for (let i = 0; i < scopes.length; i++) {
855
+ if (keyMustContainAllScopes &&
856
+ !key.includes(scopes[i].toLowerCase())) {
857
+ // When performing a cache lookup a missing scope would be a cache miss
858
+ return false;
859
+ }
860
+ else if (!keyMustContainAllScopes &&
861
+ key.includes(scopes[i].toLowerCase())) {
862
+ // When performing a cache write, any token with a subset of requested scopes should be replaced
863
+ return true;
864
+ }
865
+ }
866
+ }
867
+ return true;
868
+ }
869
+ /**
870
+ * Gets all access tokens matching the filter
871
+ * @param filter
872
+ * @returns
873
+ */
874
+ getAccessTokensByFilter(filter) {
875
+ const tokenKeys = this.getTokenKeys();
876
+ const accessTokens = [];
877
+ tokenKeys.accessToken.forEach((key) => {
878
+ if (!this.accessTokenKeyMatchesFilter(key, filter, true)) {
879
+ return;
880
+ }
881
+ const accessToken = this.getAccessTokenCredential(key);
882
+ if (accessToken &&
883
+ this.credentialMatchesFilter(accessToken, filter)) {
884
+ accessTokens.push(accessToken);
885
+ }
886
+ });
887
+ return accessTokens;
888
+ }
889
+ /**
890
+ * Helper to retrieve the appropriate refresh token from cache
891
+ * @param account {AccountInfo}
892
+ * @param familyRT {boolean}
893
+ * @param tokenKeys {?TokenKeys}
894
+ * @param performanceClient {?IPerformanceClient}
895
+ * @param correlationId {?string}
896
+ */
897
+ getRefreshToken(account, familyRT, tokenKeys, performanceClient, correlationId) {
898
+ this.commonLogger.trace("CacheManager - getRefreshToken called");
899
+ const id = familyRT ? THE_FAMILY_ID : undefined;
900
+ const refreshTokenFilter = {
901
+ homeAccountId: account.homeAccountId,
902
+ environment: account.environment,
903
+ credentialType: CredentialType.REFRESH_TOKEN,
904
+ clientId: this.clientId,
905
+ familyId: id,
906
+ };
907
+ const refreshTokenKeys = (tokenKeys && tokenKeys.refreshToken) ||
908
+ this.getTokenKeys().refreshToken;
909
+ const refreshTokens = [];
910
+ refreshTokenKeys.forEach((key) => {
911
+ // Validate key
912
+ if (this.refreshTokenKeyMatchesFilter(key, refreshTokenFilter)) {
913
+ const refreshToken = this.getRefreshTokenCredential(key);
914
+ // Validate value
915
+ if (refreshToken &&
916
+ this.credentialMatchesFilter(refreshToken, refreshTokenFilter)) {
917
+ refreshTokens.push(refreshToken);
918
+ }
919
+ }
920
+ });
921
+ const numRefreshTokens = refreshTokens.length;
922
+ if (numRefreshTokens < 1) {
923
+ this.commonLogger.info("CacheManager:getRefreshToken - No refresh token found.");
924
+ return null;
925
+ }
926
+ // address the else case after remove functions address environment aliases
927
+ if (numRefreshTokens > 1 && performanceClient && correlationId) {
928
+ performanceClient.addFields({ multiMatchedRT: numRefreshTokens }, correlationId);
929
+ }
930
+ this.commonLogger.info("CacheManager:getRefreshToken - returning refresh token");
931
+ return refreshTokens[0];
932
+ }
933
+ /**
934
+ * Validate the cache key against filter before retrieving and parsing cache value
935
+ * @param key
936
+ * @param filter
937
+ */
938
+ refreshTokenKeyMatchesFilter(inputKey, filter) {
939
+ const key = inputKey.toLowerCase();
940
+ if (filter.familyId &&
941
+ key.indexOf(filter.familyId.toLowerCase()) === -1) {
942
+ return false;
943
+ }
944
+ // If familyId is used, clientId is not in the key
945
+ if (!filter.familyId &&
946
+ filter.clientId &&
947
+ key.indexOf(filter.clientId.toLowerCase()) === -1) {
948
+ return false;
949
+ }
950
+ if (filter.homeAccountId &&
951
+ key.indexOf(filter.homeAccountId.toLowerCase()) === -1) {
952
+ return false;
953
+ }
954
+ return true;
955
+ }
956
+ /**
957
+ * Retrieve AppMetadataEntity from cache
958
+ */
959
+ readAppMetadataFromCache(environment) {
960
+ const appMetadataFilter = {
961
+ environment,
962
+ clientId: this.clientId,
963
+ };
964
+ const appMetadata = this.getAppMetadataFilteredBy(appMetadataFilter);
965
+ const appMetadataEntries = Object.keys(appMetadata).map((key) => appMetadata[key]);
966
+ const numAppMetadata = appMetadataEntries.length;
967
+ if (numAppMetadata < 1) {
968
+ return null;
969
+ }
970
+ else if (numAppMetadata > 1) {
971
+ throw createClientAuthError(multipleMatchingAppMetadata);
972
+ }
973
+ return appMetadataEntries[0];
974
+ }
975
+ /**
976
+ * Return the family_id value associated with FOCI
977
+ * @param environment
978
+ * @param clientId
979
+ */
980
+ isAppMetadataFOCI(environment) {
981
+ const appMetadata = this.readAppMetadataFromCache(environment);
982
+ return !!(appMetadata && appMetadata.familyId === THE_FAMILY_ID);
983
+ }
984
+ /**
985
+ * helper to match account ids
986
+ * @param value
987
+ * @param homeAccountId
988
+ */
989
+ matchHomeAccountId(entity, homeAccountId) {
990
+ return !!(typeof entity.homeAccountId === "string" &&
991
+ homeAccountId === entity.homeAccountId);
992
+ }
993
+ /**
994
+ * helper to match account ids
995
+ * @param entity
996
+ * @param localAccountId
997
+ * @returns
998
+ */
999
+ matchLocalAccountIdFromTokenClaims(tokenClaims, localAccountId) {
1000
+ const idTokenLocalAccountId = tokenClaims.oid || tokenClaims.sub;
1001
+ return localAccountId === idTokenLocalAccountId;
1002
+ }
1003
+ matchLocalAccountIdFromTenantProfile(tenantProfile, localAccountId) {
1004
+ return tenantProfile.localAccountId === localAccountId;
1005
+ }
1006
+ /**
1007
+ * helper to match names
1008
+ * @param entity
1009
+ * @param name
1010
+ * @returns true if the downcased name properties are present and match in the filter and the entity
1011
+ */
1012
+ matchName(claims, name) {
1013
+ return !!(name.toLowerCase() === claims.name?.toLowerCase());
1014
+ }
1015
+ /**
1016
+ * helper to match usernames
1017
+ * @param entity
1018
+ * @param username
1019
+ * @returns
1020
+ */
1021
+ matchUsername(cachedUsername, filterUsername) {
1022
+ return !!(cachedUsername &&
1023
+ typeof cachedUsername === "string" &&
1024
+ filterUsername?.toLowerCase() === cachedUsername.toLowerCase());
1025
+ }
1026
+ /**
1027
+ * helper to match assertion
1028
+ * @param value
1029
+ * @param oboAssertion
1030
+ */
1031
+ matchUserAssertionHash(entity, userAssertionHash) {
1032
+ return !!(entity.userAssertionHash &&
1033
+ userAssertionHash === entity.userAssertionHash);
1034
+ }
1035
+ /**
1036
+ * helper to match environment
1037
+ * @param value
1038
+ * @param environment
1039
+ */
1040
+ matchEnvironment(entity, environment) {
1041
+ // Check static authority options first for cases where authority metadata has not been resolved and cached yet
1042
+ if (this.staticAuthorityOptions) {
1043
+ const staticAliases = getAliasesFromStaticSources(this.staticAuthorityOptions, this.commonLogger);
1044
+ if (staticAliases.includes(environment) &&
1045
+ staticAliases.includes(entity.environment)) {
1046
+ return true;
1047
+ }
1048
+ }
1049
+ // Query metadata cache if no static authority configuration has aliases that match enviroment
1050
+ const cloudMetadata = this.getAuthorityMetadataByAlias(environment);
1051
+ if (cloudMetadata &&
1052
+ cloudMetadata.aliases.indexOf(entity.environment) > -1) {
1053
+ return true;
1054
+ }
1055
+ return false;
1056
+ }
1057
+ /**
1058
+ * helper to match credential type
1059
+ * @param entity
1060
+ * @param credentialType
1061
+ */
1062
+ matchCredentialType(entity, credentialType) {
1063
+ return (entity.credentialType &&
1064
+ credentialType.toLowerCase() === entity.credentialType.toLowerCase());
1065
+ }
1066
+ /**
1067
+ * helper to match client ids
1068
+ * @param entity
1069
+ * @param clientId
1070
+ */
1071
+ matchClientId(entity, clientId) {
1072
+ return !!(entity.clientId && clientId === entity.clientId);
1073
+ }
1074
+ /**
1075
+ * helper to match family ids
1076
+ * @param entity
1077
+ * @param familyId
1078
+ */
1079
+ matchFamilyId(entity, familyId) {
1080
+ return !!(entity.familyId && familyId === entity.familyId);
1081
+ }
1082
+ /**
1083
+ * helper to match realm
1084
+ * @param entity
1085
+ * @param realm
1086
+ */
1087
+ matchRealm(entity, realm) {
1088
+ return !!(entity.realm?.toLowerCase() === realm.toLowerCase());
1089
+ }
1090
+ /**
1091
+ * helper to match nativeAccountId
1092
+ * @param entity
1093
+ * @param nativeAccountId
1094
+ * @returns boolean indicating the match result
1095
+ */
1096
+ matchNativeAccountId(entity, nativeAccountId) {
1097
+ return !!(entity.nativeAccountId && nativeAccountId === entity.nativeAccountId);
1098
+ }
1099
+ /**
1100
+ * helper to match loginHint which can be either:
1101
+ * 1. login_hint ID token claim
1102
+ * 2. username in cached account object
1103
+ * 3. upn in ID token claims
1104
+ * @param entity
1105
+ * @param loginHint
1106
+ * @returns
1107
+ */
1108
+ matchLoginHintFromTokenClaims(tokenClaims, loginHint) {
1109
+ if (tokenClaims.login_hint === loginHint) {
1110
+ return true;
1111
+ }
1112
+ if (tokenClaims.preferred_username === loginHint) {
1113
+ return true;
1114
+ }
1115
+ if (tokenClaims.upn === loginHint) {
1116
+ return true;
1117
+ }
1118
+ return false;
1119
+ }
1120
+ /**
1121
+ * Helper to match sid
1122
+ * @param entity
1123
+ * @param sid
1124
+ * @returns true if the sid claim is present and matches the filter
1125
+ */
1126
+ matchSid(idTokenClaims, sid) {
1127
+ return idTokenClaims.sid === sid;
1128
+ }
1129
+ matchAuthorityType(entity, authorityType) {
1130
+ return !!(entity.authorityType &&
1131
+ authorityType.toLowerCase() === entity.authorityType.toLowerCase());
1132
+ }
1133
+ /**
1134
+ * Returns true if the target scopes are a subset of the current entity's scopes, false otherwise.
1135
+ * @param entity
1136
+ * @param target
1137
+ */
1138
+ matchTarget(entity, target) {
1139
+ const isNotAccessTokenCredential = entity.credentialType !== CredentialType.ACCESS_TOKEN &&
1140
+ entity.credentialType !==
1141
+ CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME;
1142
+ if (isNotAccessTokenCredential || !entity.target) {
1143
+ return false;
1144
+ }
1145
+ const entityScopeSet = ScopeSet.fromString(entity.target);
1146
+ return entityScopeSet.containsScopeSet(target);
1147
+ }
1148
+ /**
1149
+ * Returns true if the credential's tokenType or Authentication Scheme matches the one in the request, false otherwise
1150
+ * @param entity
1151
+ * @param tokenType
1152
+ */
1153
+ matchTokenType(entity, tokenType) {
1154
+ return !!(entity.tokenType && entity.tokenType === tokenType);
1155
+ }
1156
+ /**
1157
+ * Returns true if the credential's keyId matches the one in the request, false otherwise
1158
+ * @param entity
1159
+ * @param tokenType
1160
+ */
1161
+ matchKeyId(entity, keyId) {
1162
+ return !!(entity.keyId && entity.keyId === keyId);
1163
+ }
1164
+ /**
1165
+ * returns if a given cache entity is of the type appmetadata
1166
+ * @param key
1167
+ */
1168
+ isAppMetadata(key) {
1169
+ return key.indexOf(APP_METADATA) !== -1;
1170
+ }
1171
+ /**
1172
+ * returns if a given cache entity is of the type authoritymetadata
1173
+ * @param key
1174
+ */
1175
+ isAuthorityMetadata(key) {
1176
+ return key.indexOf(AUTHORITY_METADATA_CONSTANTS.CACHE_KEY) !== -1;
1177
+ }
1178
+ /**
1179
+ * returns cache key used for cloud instance metadata
1180
+ */
1181
+ generateAuthorityMetadataCacheKey(authority) {
1182
+ return `${AUTHORITY_METADATA_CONSTANTS.CACHE_KEY}-${this.clientId}-${authority}`;
1183
+ }
1184
+ /**
1185
+ * Helper to convert serialized data to object
1186
+ * @param obj
1187
+ * @param json
1188
+ */
1189
+ static toObject(obj, json) {
1190
+ for (const propertyName in json) {
1191
+ obj[propertyName] = json[propertyName];
1192
+ }
1193
+ return obj;
1194
+ }
1195
+ }
1196
+ /** @internal */
1197
+ class DefaultStorageClass extends CacheManager {
1198
+ setAccount() {
1199
+ throw createClientAuthError(methodNotImplemented);
1200
+ }
1201
+ getAccount() {
1202
+ throw createClientAuthError(methodNotImplemented);
1203
+ }
1204
+ getCachedAccountEntity() {
1205
+ throw createClientAuthError(methodNotImplemented);
1206
+ }
1207
+ setIdTokenCredential() {
1208
+ throw createClientAuthError(methodNotImplemented);
1209
+ }
1210
+ getIdTokenCredential() {
1211
+ throw createClientAuthError(methodNotImplemented);
1212
+ }
1213
+ setAccessTokenCredential() {
1214
+ throw createClientAuthError(methodNotImplemented);
1215
+ }
1216
+ getAccessTokenCredential() {
1217
+ throw createClientAuthError(methodNotImplemented);
1218
+ }
1219
+ setRefreshTokenCredential() {
1220
+ throw createClientAuthError(methodNotImplemented);
1221
+ }
1222
+ getRefreshTokenCredential() {
1223
+ throw createClientAuthError(methodNotImplemented);
1224
+ }
1225
+ setAppMetadata() {
1226
+ throw createClientAuthError(methodNotImplemented);
1227
+ }
1228
+ getAppMetadata() {
1229
+ throw createClientAuthError(methodNotImplemented);
1230
+ }
1231
+ setServerTelemetry() {
1232
+ throw createClientAuthError(methodNotImplemented);
1233
+ }
1234
+ getServerTelemetry() {
1235
+ throw createClientAuthError(methodNotImplemented);
1236
+ }
1237
+ setAuthorityMetadata() {
1238
+ throw createClientAuthError(methodNotImplemented);
1239
+ }
1240
+ getAuthorityMetadata() {
1241
+ throw createClientAuthError(methodNotImplemented);
1242
+ }
1243
+ getAuthorityMetadataKeys() {
1244
+ throw createClientAuthError(methodNotImplemented);
1245
+ }
1246
+ setThrottlingCache() {
1247
+ throw createClientAuthError(methodNotImplemented);
1248
+ }
1249
+ getThrottlingCache() {
1250
+ throw createClientAuthError(methodNotImplemented);
1251
+ }
1252
+ removeItem() {
1253
+ throw createClientAuthError(methodNotImplemented);
1254
+ }
1255
+ containsKey() {
1256
+ throw createClientAuthError(methodNotImplemented);
1257
+ }
1258
+ getKeys() {
1259
+ throw createClientAuthError(methodNotImplemented);
1260
+ }
1261
+ getAccountKeys() {
1262
+ throw createClientAuthError(methodNotImplemented);
1263
+ }
1264
+ getTokenKeys() {
1265
+ throw createClientAuthError(methodNotImplemented);
1266
+ }
1267
+ async clear() {
1268
+ throw createClientAuthError(methodNotImplemented);
1269
+ }
1270
+ updateCredentialCacheKey() {
1271
+ throw createClientAuthError(methodNotImplemented);
1272
+ }
1273
+ removeOutdatedAccount() {
1274
+ throw createClientAuthError(methodNotImplemented);
1275
+ }
1114
1276
  }
1115
1277
 
1116
1278
  export { CacheManager, DefaultStorageClass };