@azure/msal-common 14.1.0-alpha.0 → 14.2.0

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