@azure/msal-common 14.6.0-alpha.0 → 14.6.1

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