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