@azure/msal-common 14.4.1-beta.1 → 14.6.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account/AccountInfo.d.ts +27 -0
- package/dist/account/AccountInfo.d.ts.map +1 -1
- package/dist/account/AccountInfo.mjs +64 -0
- package/dist/account/AccountInfo.mjs.map +1 -0
- package/dist/account/AuthToken.mjs +1 -1
- package/dist/account/AuthToken.mjs.map +1 -1
- package/dist/account/CcsCredential.mjs +1 -1
- package/dist/account/CcsCredential.mjs.map +1 -1
- package/dist/account/ClientInfo.d.ts +1 -2
- package/dist/account/ClientInfo.d.ts.map +1 -1
- package/dist/account/ClientInfo.mjs +3 -3
- package/dist/account/ClientInfo.mjs.map +1 -1
- package/dist/account/TokenClaims.d.ts +18 -0
- package/dist/account/TokenClaims.d.ts.map +1 -1
- package/dist/account/TokenClaims.mjs +25 -0
- package/dist/account/TokenClaims.mjs.map +1 -0
- package/dist/authority/Authority.d.ts +7 -2
- package/dist/authority/Authority.d.ts.map +1 -1
- package/dist/authority/Authority.mjs +34 -7
- package/dist/authority/Authority.mjs.map +1 -1
- package/dist/authority/AuthorityFactory.mjs +1 -1
- package/dist/authority/AuthorityFactory.mjs.map +1 -1
- package/dist/authority/AuthorityMetadata.mjs +1 -1
- package/dist/authority/AuthorityMetadata.mjs.map +1 -1
- package/dist/authority/AuthorityOptions.mjs +1 -1
- package/dist/authority/AuthorityOptions.mjs.map +1 -1
- package/dist/authority/AuthorityType.mjs +1 -1
- package/dist/authority/AuthorityType.mjs.map +1 -1
- package/dist/authority/CloudInstanceDiscoveryErrorResponse.mjs +1 -1
- package/dist/authority/CloudInstanceDiscoveryErrorResponse.mjs.map +1 -1
- package/dist/authority/CloudInstanceDiscoveryResponse.mjs +1 -1
- package/dist/authority/CloudInstanceDiscoveryResponse.mjs.map +1 -1
- package/dist/authority/OpenIdConfigResponse.mjs +1 -1
- package/dist/authority/OpenIdConfigResponse.mjs.map +1 -1
- package/dist/authority/ProtocolMode.mjs +1 -1
- package/dist/authority/ProtocolMode.mjs.map +1 -1
- package/dist/authority/RegionDiscovery.mjs +1 -1
- package/dist/authority/RegionDiscovery.mjs.map +1 -1
- package/dist/cache/CacheManager.d.ts +50 -27
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.mjs +243 -107
- package/dist/cache/CacheManager.mjs.map +1 -1
- package/dist/cache/entities/AccountEntity.d.ts +9 -4
- package/dist/cache/entities/AccountEntity.d.ts.map +1 -1
- package/dist/cache/entities/AccountEntity.mjs +56 -28
- package/dist/cache/entities/AccountEntity.mjs.map +1 -1
- package/dist/cache/entities/AppMetadataEntity.mjs +1 -1
- package/dist/cache/entities/AppMetadataEntity.mjs.map +1 -1
- package/dist/cache/entities/AuthorityMetadataEntity.mjs +1 -1
- package/dist/cache/entities/AuthorityMetadataEntity.mjs.map +1 -1
- package/dist/cache/entities/CacheRecord.mjs +1 -1
- package/dist/cache/entities/CacheRecord.mjs.map +1 -1
- package/dist/cache/entities/ThrottlingEntity.d.ts +2 -8
- package/dist/cache/entities/ThrottlingEntity.d.ts.map +1 -1
- package/dist/cache/persistence/TokenCacheContext.mjs +1 -1
- package/dist/cache/persistence/TokenCacheContext.mjs.map +1 -1
- package/dist/cache/utils/CacheHelpers.d.ts +7 -1
- package/dist/cache/utils/CacheHelpers.d.ts.map +1 -1
- package/dist/cache/utils/CacheHelpers.mjs +20 -4
- package/dist/cache/utils/CacheHelpers.mjs.map +1 -1
- package/dist/cache/utils/CacheTypes.d.ts +2 -0
- package/dist/cache/utils/CacheTypes.d.ts.map +1 -1
- package/dist/client/AuthorizationCodeClient.d.ts.map +1 -1
- package/dist/client/AuthorizationCodeClient.mjs +10 -9
- package/dist/client/AuthorizationCodeClient.mjs.map +1 -1
- package/dist/client/BaseClient.mjs +1 -1
- package/dist/client/BaseClient.mjs.map +1 -1
- package/dist/client/RefreshTokenClient.d.ts.map +1 -1
- package/dist/client/RefreshTokenClient.mjs +25 -11
- package/dist/client/RefreshTokenClient.mjs.map +1 -1
- package/dist/client/SilentFlowClient.d.ts.map +1 -1
- package/dist/client/SilentFlowClient.mjs +20 -9
- package/dist/client/SilentFlowClient.mjs.map +1 -1
- package/dist/config/ClientConfiguration.d.ts.map +1 -1
- package/dist/config/ClientConfiguration.mjs +2 -4
- package/dist/config/ClientConfiguration.mjs.map +1 -1
- package/dist/constants/AADServerParamKeys.d.ts +53 -0
- package/dist/constants/AADServerParamKeys.d.ts.map +1 -0
- package/dist/constants/AADServerParamKeys.mjs +61 -0
- package/dist/constants/AADServerParamKeys.mjs.map +1 -0
- package/dist/crypto/ICrypto.mjs +1 -1
- package/dist/crypto/ICrypto.mjs.map +1 -1
- package/dist/crypto/JoseHeader.mjs +1 -1
- package/dist/crypto/JoseHeader.mjs.map +1 -1
- package/dist/crypto/PopTokenGenerator.mjs +2 -2
- package/dist/crypto/PopTokenGenerator.mjs.map +1 -1
- package/dist/error/AuthError.mjs +1 -1
- package/dist/error/AuthError.mjs.map +1 -1
- package/dist/error/AuthErrorCodes.mjs +1 -1
- package/dist/error/AuthErrorCodes.mjs.map +1 -1
- package/dist/error/ClientAuthError.mjs +1 -1
- package/dist/error/ClientAuthError.mjs.map +1 -1
- package/dist/error/ClientAuthErrorCodes.mjs +1 -1
- package/dist/error/ClientAuthErrorCodes.mjs.map +1 -1
- package/dist/error/ClientConfigurationError.mjs +1 -1
- package/dist/error/ClientConfigurationError.mjs.map +1 -1
- package/dist/error/ClientConfigurationErrorCodes.mjs +1 -1
- package/dist/error/ClientConfigurationErrorCodes.mjs.map +1 -1
- package/dist/error/InteractionRequiredAuthError.d.ts +4 -0
- package/dist/error/InteractionRequiredAuthError.d.ts.map +1 -1
- package/dist/error/InteractionRequiredAuthError.mjs +10 -3
- package/dist/error/InteractionRequiredAuthError.mjs.map +1 -1
- package/dist/error/InteractionRequiredAuthErrorCodes.d.ts +1 -0
- package/dist/error/InteractionRequiredAuthErrorCodes.d.ts.map +1 -1
- package/dist/error/InteractionRequiredAuthErrorCodes.mjs +4 -3
- package/dist/error/InteractionRequiredAuthErrorCodes.mjs.map +1 -1
- package/dist/error/JoseHeaderError.mjs +1 -1
- package/dist/error/JoseHeaderError.mjs.map +1 -1
- package/dist/error/JoseHeaderErrorCodes.mjs +1 -1
- package/dist/error/JoseHeaderErrorCodes.mjs.map +1 -1
- package/dist/error/ServerError.mjs +1 -1
- package/dist/error/ServerError.mjs.map +1 -1
- package/dist/index.cjs +797 -416
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +7 -4
- package/dist/index.mjs.map +1 -1
- package/dist/logger/Logger.mjs +1 -1
- package/dist/logger/Logger.mjs.map +1 -1
- package/dist/network/INetworkModule.mjs +1 -1
- package/dist/network/INetworkModule.mjs.map +1 -1
- package/dist/network/NetworkManager.mjs +1 -1
- package/dist/network/NetworkManager.mjs.map +1 -1
- package/dist/network/ThrottlingUtils.mjs +1 -1
- package/dist/network/ThrottlingUtils.mjs.map +1 -1
- package/dist/packageMetadata.d.ts +1 -1
- package/dist/packageMetadata.d.ts.map +1 -1
- package/dist/packageMetadata.mjs +2 -2
- package/dist/packageMetadata.mjs.map +1 -1
- package/dist/request/AuthenticationHeaderParser.mjs +1 -1
- package/dist/request/AuthenticationHeaderParser.mjs.map +1 -1
- package/dist/request/CommonRefreshTokenRequest.d.ts +0 -1
- package/dist/request/CommonRefreshTokenRequest.d.ts.map +1 -1
- package/dist/request/CommonSilentFlowRequest.d.ts +7 -1
- package/dist/request/CommonSilentFlowRequest.d.ts.map +1 -1
- package/dist/request/RequestParameterBuilder.d.ts.map +1 -1
- package/dist/request/RequestParameterBuilder.mjs +46 -45
- package/dist/request/RequestParameterBuilder.mjs.map +1 -1
- package/dist/request/RequestValidator.mjs +1 -1
- package/dist/request/RequestValidator.mjs.map +1 -1
- package/dist/request/ScopeSet.mjs +1 -1
- package/dist/request/ScopeSet.mjs.map +1 -1
- package/dist/response/ResponseHandler.d.ts +3 -1
- package/dist/response/ResponseHandler.d.ts.map +1 -1
- package/dist/response/ResponseHandler.mjs +56 -20
- package/dist/response/ResponseHandler.mjs.map +1 -1
- package/dist/response/ServerAuthorizationTokenResponse.d.ts +1 -0
- package/dist/response/ServerAuthorizationTokenResponse.d.ts.map +1 -1
- package/dist/telemetry/performance/IPerformanceClient.d.ts +6 -0
- package/dist/telemetry/performance/IPerformanceClient.d.ts.map +1 -1
- package/dist/telemetry/performance/PerformanceClient.d.ts +9 -2
- package/dist/telemetry/performance/PerformanceClient.d.ts.map +1 -1
- package/dist/telemetry/performance/PerformanceClient.mjs +17 -13
- package/dist/telemetry/performance/PerformanceClient.mjs.map +1 -1
- package/dist/telemetry/performance/PerformanceEvent.mjs +1 -1
- package/dist/telemetry/performance/PerformanceEvent.mjs.map +1 -1
- package/dist/telemetry/performance/StubPerformanceClient.d.ts.map +1 -1
- package/dist/telemetry/performance/StubPerformanceClient.mjs +1 -1
- package/dist/telemetry/performance/StubPerformanceClient.mjs.map +1 -1
- package/dist/telemetry/server/ServerTelemetryManager.mjs +1 -1
- package/dist/telemetry/server/ServerTelemetryManager.mjs.map +1 -1
- package/dist/url/UrlString.mjs +1 -1
- package/dist/url/UrlString.mjs.map +1 -1
- package/dist/utils/Constants.d.ts +2 -69
- package/dist/utils/Constants.d.ts.map +1 -1
- package/dist/utils/Constants.mjs +6 -70
- package/dist/utils/Constants.mjs.map +1 -1
- package/dist/utils/FunctionWrappers.mjs +1 -1
- package/dist/utils/FunctionWrappers.mjs.map +1 -1
- package/dist/utils/ProtocolUtils.mjs +1 -1
- package/dist/utils/ProtocolUtils.mjs.map +1 -1
- package/dist/utils/StringUtils.mjs +1 -1
- package/dist/utils/StringUtils.mjs.map +1 -1
- package/dist/utils/TimeUtils.mjs +1 -1
- package/dist/utils/TimeUtils.mjs.map +1 -1
- package/dist/utils/UrlUtils.mjs +1 -1
- package/dist/utils/UrlUtils.mjs.map +1 -1
- package/package.json +93 -92
- package/src/account/AccountInfo.ts +96 -0
- package/src/account/ClientInfo.ts +2 -3
- package/src/account/TokenClaims.ts +28 -0
- package/src/authority/Authority.ts +38 -5
- package/src/cache/CacheManager.ts +410 -162
- package/src/cache/entities/AccountEntity.ts +84 -33
- package/src/cache/entities/ThrottlingEntity.ts +2 -24
- package/src/cache/utils/CacheHelpers.ts +22 -2
- package/src/cache/utils/CacheTypes.ts +11 -0
- package/src/client/AuthorizationCodeClient.ts +4 -5
- package/src/client/RefreshTokenClient.ts +35 -14
- package/src/client/SilentFlowClient.ts +31 -11
- package/src/config/ClientConfiguration.ts +4 -4
- package/src/constants/AADServerParamKeys.ts +57 -0
- package/src/crypto/PopTokenGenerator.ts +1 -1
- package/src/error/InteractionRequiredAuthError.ts +12 -2
- package/src/error/InteractionRequiredAuthErrorCodes.ts +1 -0
- package/src/index.ts +18 -5
- package/src/packageMetadata.ts +1 -1
- package/src/request/CommonRefreshTokenRequest.ts +0 -1
- package/src/request/CommonSilentFlowRequest.ts +7 -1
- package/src/request/RequestParameterBuilder.ts +7 -5
- package/src/response/ResponseHandler.ts +109 -25
- package/src/response/ServerAuthorizationTokenResponse.ts +1 -0
- package/src/telemetry/performance/IPerformanceClient.ts +6 -0
- package/src/telemetry/performance/PerformanceClient.ts +24 -31
- package/src/telemetry/performance/StubPerformanceClient.ts +0 -1
- package/src/utils/Constants.ts +4 -72
- package/dist/cache/entities/ThrottlingEntity.mjs +0 -30
- package/dist/cache/entities/ThrottlingEntity.mjs.map +0 -1
|
@@ -3,18 +3,25 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { CacheAccountType, Separators } from "../../utils/Constants";
|
|
7
7
|
import { Authority } from "../../authority/Authority";
|
|
8
8
|
import { ICrypto } from "../../crypto/ICrypto";
|
|
9
|
-
import { buildClientInfo } from "../../account/ClientInfo";
|
|
10
|
-
import {
|
|
9
|
+
import { ClientInfo, buildClientInfo } from "../../account/ClientInfo";
|
|
10
|
+
import {
|
|
11
|
+
AccountInfo,
|
|
12
|
+
TenantProfile,
|
|
13
|
+
buildTenantProfileFromIdTokenClaims,
|
|
14
|
+
} from "../../account/AccountInfo";
|
|
11
15
|
import {
|
|
12
16
|
createClientAuthError,
|
|
13
17
|
ClientAuthErrorCodes,
|
|
14
18
|
} from "../../error/ClientAuthError";
|
|
15
19
|
import { AuthorityType } from "../../authority/AuthorityType";
|
|
16
20
|
import { Logger } from "../../logger/Logger";
|
|
17
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
TokenClaims,
|
|
23
|
+
getTenantIdFromIdTokenClaims,
|
|
24
|
+
} from "../../account/TokenClaims";
|
|
18
25
|
import { ProtocolMode } from "../../authority/ProtocolMode";
|
|
19
26
|
|
|
20
27
|
/**
|
|
@@ -35,8 +42,8 @@ import { ProtocolMode } from "../../authority/ProtocolMode";
|
|
|
35
42
|
* name: Full name for the account, including given name and family name,
|
|
36
43
|
* lastModificationTime: last time this entity was modified in the cache
|
|
37
44
|
* lastModificationApp:
|
|
38
|
-
* idTokenClaims: Object containing claims parsed from ID token
|
|
39
45
|
* nativeAccountId: Account identifier on the native device
|
|
46
|
+
* tenantProfiles: Array of tenant profile objects for each tenant that the account has authenticated with in the browser
|
|
40
47
|
* }
|
|
41
48
|
* @internal
|
|
42
49
|
*/
|
|
@@ -53,8 +60,8 @@ export class AccountEntity {
|
|
|
53
60
|
lastModificationApp?: string;
|
|
54
61
|
cloudGraphHostName?: string;
|
|
55
62
|
msGraphHost?: string;
|
|
56
|
-
idTokenClaims?: TokenClaims;
|
|
57
63
|
nativeAccountId?: string;
|
|
64
|
+
tenantProfiles?: Array<TenantProfile>;
|
|
58
65
|
|
|
59
66
|
/**
|
|
60
67
|
* Generate Account Id key component as per the schema: <home_account_id>-<environment>
|
|
@@ -88,21 +95,34 @@ export class AccountEntity {
|
|
|
88
95
|
username: this.username,
|
|
89
96
|
localAccountId: this.localAccountId,
|
|
90
97
|
name: this.name,
|
|
91
|
-
idTokenClaims: this.idTokenClaims,
|
|
92
98
|
nativeAccountId: this.nativeAccountId,
|
|
93
99
|
authorityType: this.authorityType,
|
|
100
|
+
// Deserialize tenant profiles array into a Map
|
|
101
|
+
tenantProfiles: new Map(
|
|
102
|
+
(this.tenantProfiles || []).map((tenantProfile) => {
|
|
103
|
+
return [tenantProfile.tenantId, tenantProfile];
|
|
104
|
+
})
|
|
105
|
+
),
|
|
94
106
|
};
|
|
95
107
|
}
|
|
96
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Returns true if the account entity is in single tenant format (outdated), false otherwise
|
|
111
|
+
*/
|
|
112
|
+
isSingleTenant(): boolean {
|
|
113
|
+
return !this.tenantProfiles;
|
|
114
|
+
}
|
|
115
|
+
|
|
97
116
|
/**
|
|
98
117
|
* Generates account key from interface
|
|
99
118
|
* @param accountInterface
|
|
100
119
|
*/
|
|
101
120
|
static generateAccountCacheKey(accountInterface: AccountInfo): string {
|
|
121
|
+
const homeTenantId = accountInterface.homeAccountId.split(".")[1];
|
|
102
122
|
const accountKey = [
|
|
103
123
|
accountInterface.homeAccountId,
|
|
104
|
-
accountInterface.environment ||
|
|
105
|
-
accountInterface.tenantId ||
|
|
124
|
+
accountInterface.environment || "",
|
|
125
|
+
homeTenantId || accountInterface.tenantId || "",
|
|
106
126
|
];
|
|
107
127
|
|
|
108
128
|
return accountKey.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase();
|
|
@@ -121,8 +141,10 @@ export class AccountEntity {
|
|
|
121
141
|
msGraphHost?: string;
|
|
122
142
|
environment?: string;
|
|
123
143
|
nativeAccountId?: string;
|
|
144
|
+
tenantProfiles?: Array<TenantProfile>;
|
|
124
145
|
},
|
|
125
|
-
authority: Authority
|
|
146
|
+
authority: Authority,
|
|
147
|
+
base64Decode?: (input: string) => string
|
|
126
148
|
): AccountEntity {
|
|
127
149
|
const account: AccountEntity = new AccountEntity();
|
|
128
150
|
|
|
@@ -134,6 +156,15 @@ export class AccountEntity {
|
|
|
134
156
|
account.authorityType = CacheAccountType.GENERIC_ACCOUNT_TYPE;
|
|
135
157
|
}
|
|
136
158
|
|
|
159
|
+
let clientInfo: ClientInfo | undefined;
|
|
160
|
+
|
|
161
|
+
if (accountDetails.clientInfo && base64Decode) {
|
|
162
|
+
clientInfo = buildClientInfo(
|
|
163
|
+
accountDetails.clientInfo,
|
|
164
|
+
base64Decode
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
137
168
|
account.clientInfo = accountDetails.clientInfo;
|
|
138
169
|
account.homeAccountId = accountDetails.homeAccountId;
|
|
139
170
|
account.nativeAccountId = accountDetails.nativeAccountId;
|
|
@@ -151,13 +182,16 @@ export class AccountEntity {
|
|
|
151
182
|
account.environment = env;
|
|
152
183
|
// non AAD scenarios can have empty realm
|
|
153
184
|
account.realm =
|
|
154
|
-
|
|
185
|
+
clientInfo?.utid ||
|
|
186
|
+
getTenantIdFromIdTokenClaims(accountDetails.idTokenClaims) ||
|
|
187
|
+
"";
|
|
155
188
|
|
|
156
189
|
// How do you account for MSA CID here?
|
|
157
190
|
account.localAccountId =
|
|
191
|
+
clientInfo?.uid ||
|
|
158
192
|
accountDetails.idTokenClaims.oid ||
|
|
159
193
|
accountDetails.idTokenClaims.sub ||
|
|
160
|
-
|
|
194
|
+
"";
|
|
161
195
|
|
|
162
196
|
/*
|
|
163
197
|
* In B2C scenarios the emails claim is used instead of preferred_username and it is an array.
|
|
@@ -171,12 +205,26 @@ export class AccountEntity {
|
|
|
171
205
|
? accountDetails.idTokenClaims.emails[0]
|
|
172
206
|
: null;
|
|
173
207
|
|
|
174
|
-
account.username = preferredUsername || email ||
|
|
208
|
+
account.username = preferredUsername || email || "";
|
|
175
209
|
account.name = accountDetails.idTokenClaims.name;
|
|
176
210
|
|
|
177
211
|
account.cloudGraphHostName = accountDetails.cloudGraphHostName;
|
|
178
212
|
account.msGraphHost = accountDetails.msGraphHost;
|
|
179
213
|
|
|
214
|
+
if (accountDetails.tenantProfiles) {
|
|
215
|
+
account.tenantProfiles = accountDetails.tenantProfiles;
|
|
216
|
+
} else {
|
|
217
|
+
const tenantProfiles = [];
|
|
218
|
+
if (accountDetails.idTokenClaims) {
|
|
219
|
+
const tenantProfile = buildTenantProfileFromIdTokenClaims(
|
|
220
|
+
accountDetails.homeAccountId,
|
|
221
|
+
accountDetails.idTokenClaims
|
|
222
|
+
);
|
|
223
|
+
tenantProfiles.push(tenantProfile);
|
|
224
|
+
}
|
|
225
|
+
account.tenantProfiles = tenantProfiles;
|
|
226
|
+
}
|
|
227
|
+
|
|
180
228
|
return account;
|
|
181
229
|
}
|
|
182
230
|
|
|
@@ -208,6 +256,10 @@ export class AccountEntity {
|
|
|
208
256
|
|
|
209
257
|
account.cloudGraphHostName = cloudGraphHostName;
|
|
210
258
|
account.msGraphHost = msGraphHost;
|
|
259
|
+
// Serialize tenant profiles map into an array
|
|
260
|
+
account.tenantProfiles = Array.from(
|
|
261
|
+
accountInfo.tenantProfiles?.values() || []
|
|
262
|
+
);
|
|
211
263
|
|
|
212
264
|
return account;
|
|
213
265
|
}
|
|
@@ -224,31 +276,30 @@ export class AccountEntity {
|
|
|
224
276
|
cryptoObj: ICrypto,
|
|
225
277
|
idTokenClaims?: TokenClaims
|
|
226
278
|
): string {
|
|
227
|
-
|
|
228
|
-
? idTokenClaims.sub
|
|
229
|
-
: Constants.EMPTY_STRING;
|
|
230
|
-
|
|
231
|
-
// since ADFS does not have tid and does not set client_info
|
|
279
|
+
// since ADFS/DSTS do not have tid and does not set client_info
|
|
232
280
|
if (
|
|
233
|
-
|
|
234
|
-
|
|
281
|
+
!(
|
|
282
|
+
authType === AuthorityType.Adfs ||
|
|
283
|
+
authType === AuthorityType.Dsts
|
|
284
|
+
)
|
|
235
285
|
) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
286
|
+
// for cases where there is clientInfo
|
|
287
|
+
if (serverClientInfo) {
|
|
288
|
+
try {
|
|
289
|
+
const clientInfo = buildClientInfo(
|
|
290
|
+
serverClientInfo,
|
|
291
|
+
cryptoObj.base64Decode
|
|
292
|
+
);
|
|
293
|
+
if (clientInfo.uid && clientInfo.utid) {
|
|
294
|
+
return `${clientInfo.uid}.${clientInfo.utid}`;
|
|
295
|
+
}
|
|
296
|
+
} catch (e) {}
|
|
297
|
+
}
|
|
298
|
+
logger.warning("No client info in response");
|
|
247
299
|
}
|
|
248
300
|
|
|
249
301
|
// default to "sub" claim
|
|
250
|
-
|
|
251
|
-
return accountId;
|
|
302
|
+
return idTokenClaims?.sub || "";
|
|
252
303
|
}
|
|
253
304
|
|
|
254
305
|
/**
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export class ThrottlingEntity {
|
|
6
|
+
export type ThrottlingEntity = {
|
|
9
7
|
// Unix-time value representing the expiration of the throttle
|
|
10
8
|
throttleTime: number;
|
|
11
9
|
// Information provided by the server
|
|
@@ -13,24 +11,4 @@ export class ThrottlingEntity {
|
|
|
13
11
|
errorCodes?: Array<string>;
|
|
14
12
|
errorMessage?: string;
|
|
15
13
|
subError?: string;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* validates if a given cache entry is "Throttling", parses <key,value>
|
|
19
|
-
* @param key
|
|
20
|
-
* @param entity
|
|
21
|
-
*/
|
|
22
|
-
static isThrottlingEntity(key: string, entity?: object): boolean {
|
|
23
|
-
let validateKey: boolean = false;
|
|
24
|
-
if (key) {
|
|
25
|
-
validateKey =
|
|
26
|
-
key.indexOf(ThrottlingConstants.THROTTLING_PREFIX) === 0;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let validateEntity: boolean = true;
|
|
30
|
-
if (entity) {
|
|
31
|
-
validateEntity = entity.hasOwnProperty("throttleTime");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return validateKey && validateEntity;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
14
|
+
};
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
CredentialType,
|
|
15
15
|
SERVER_TELEM_CONSTANTS,
|
|
16
16
|
Separators,
|
|
17
|
+
ThrottlingConstants,
|
|
17
18
|
} from "../../utils/Constants";
|
|
18
19
|
import { TimeUtils } from "../../utils/TimeUtils";
|
|
19
20
|
import { AccessTokenEntity } from "../entities/AccessTokenEntity";
|
|
@@ -169,7 +170,7 @@ export function createRefreshTokenEntity(
|
|
|
169
170
|
clientId: string,
|
|
170
171
|
familyId?: string,
|
|
171
172
|
userAssertionHash?: string,
|
|
172
|
-
expiresOn?:
|
|
173
|
+
expiresOn?: number
|
|
173
174
|
): RefreshTokenEntity {
|
|
174
175
|
const rtEntity: RefreshTokenEntity = {
|
|
175
176
|
credentialType: CredentialType.REFRESH_TOKEN,
|
|
@@ -188,7 +189,7 @@ export function createRefreshTokenEntity(
|
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
if (expiresOn) {
|
|
191
|
-
rtEntity.expiresOn = expiresOn;
|
|
192
|
+
rtEntity.expiresOn = expiresOn.toString();
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
return rtEntity;
|
|
@@ -330,3 +331,22 @@ export function isServerTelemetryEntity(key: string, entity?: object): boolean {
|
|
|
330
331
|
|
|
331
332
|
return validateKey && validateEntity;
|
|
332
333
|
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* validates if a given cache entry is "Throttling", parses <key,value>
|
|
337
|
+
* @param key
|
|
338
|
+
* @param entity
|
|
339
|
+
*/
|
|
340
|
+
export function isThrottlingEntity(key: string, entity?: object): boolean {
|
|
341
|
+
let validateKey: boolean = false;
|
|
342
|
+
if (key) {
|
|
343
|
+
validateKey = key.indexOf(ThrottlingConstants.THROTTLING_PREFIX) === 0;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
let validateEntity: boolean = true;
|
|
347
|
+
if (entity) {
|
|
348
|
+
validateEntity = entity.hasOwnProperty("throttleTime");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return validateKey && validateEntity;
|
|
352
|
+
}
|
|
@@ -60,8 +60,19 @@ export type AccountFilter = Omit<
|
|
|
60
60
|
realm?: string;
|
|
61
61
|
loginHint?: string;
|
|
62
62
|
sid?: string;
|
|
63
|
+
isHomeTenant?: boolean;
|
|
63
64
|
};
|
|
64
65
|
|
|
66
|
+
export type TenantProfileFilter = Pick<
|
|
67
|
+
AccountFilter,
|
|
68
|
+
| "localAccountId"
|
|
69
|
+
| "loginHint"
|
|
70
|
+
| "name"
|
|
71
|
+
| "sid"
|
|
72
|
+
| "isHomeTenant"
|
|
73
|
+
| "username"
|
|
74
|
+
>;
|
|
75
|
+
|
|
65
76
|
/**
|
|
66
77
|
* Credential: <home_account_id*>-<environment>-<credential_type>-<client_id>-<realm*>-<target*>-<scheme*>
|
|
67
78
|
*/
|
|
@@ -13,9 +13,9 @@ import {
|
|
|
13
13
|
AuthenticationScheme,
|
|
14
14
|
PromptValue,
|
|
15
15
|
Separators,
|
|
16
|
-
AADServerParamKeys,
|
|
17
16
|
HeaderNames,
|
|
18
17
|
} from "../utils/Constants";
|
|
18
|
+
import * as AADServerParamKeys from "../constants/AADServerParamKeys";
|
|
19
19
|
import {
|
|
20
20
|
ClientConfiguration,
|
|
21
21
|
isOidcProtocolMode,
|
|
@@ -161,8 +161,7 @@ export class AuthorizationCodeClient extends BaseClient {
|
|
|
161
161
|
undefined,
|
|
162
162
|
undefined,
|
|
163
163
|
undefined,
|
|
164
|
-
requestId
|
|
165
|
-
(TimeUtils.nowSeconds() + 24 * 60 * 60).toString()
|
|
164
|
+
requestId
|
|
166
165
|
);
|
|
167
166
|
}
|
|
168
167
|
|
|
@@ -255,7 +254,7 @@ export class AuthorizationCodeClient extends BaseClient {
|
|
|
255
254
|
try {
|
|
256
255
|
const clientInfo = buildClientInfo(
|
|
257
256
|
request.clientInfo,
|
|
258
|
-
this.cryptoUtils
|
|
257
|
+
this.cryptoUtils.base64Decode
|
|
259
258
|
);
|
|
260
259
|
ccsCredential = {
|
|
261
260
|
credential: `${clientInfo.uid}${Separators.CLIENT_INFO_SEPARATOR}${clientInfo.utid}`,
|
|
@@ -422,7 +421,7 @@ export class AuthorizationCodeClient extends BaseClient {
|
|
|
422
421
|
try {
|
|
423
422
|
const clientInfo = buildClientInfo(
|
|
424
423
|
request.clientInfo,
|
|
425
|
-
this.cryptoUtils
|
|
424
|
+
this.cryptoUtils.base64Decode
|
|
426
425
|
);
|
|
427
426
|
ccsCred = {
|
|
428
427
|
credential: `${clientInfo.uid}${Separators.CLIENT_INFO_SEPARATOR}${clientInfo.utid}`,
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
AuthenticationScheme,
|
|
18
18
|
Errors,
|
|
19
19
|
HeaderNames,
|
|
20
|
-
AADServerParamKeys,
|
|
21
20
|
} from "../utils/Constants";
|
|
21
|
+
import * as AADServerParamKeys from "../constants/AADServerParamKeys";
|
|
22
22
|
import { ResponseHandler } from "../response/ResponseHandler";
|
|
23
23
|
import { AuthenticationResult } from "../response/AuthenticationResult";
|
|
24
24
|
import { PopTokenGenerator } from "../crypto/PopTokenGenerator";
|
|
@@ -47,6 +47,10 @@ import {
|
|
|
47
47
|
import { PerformanceEvents } from "../telemetry/performance/PerformanceEvent";
|
|
48
48
|
import { IPerformanceClient } from "../telemetry/performance/IPerformanceClient";
|
|
49
49
|
import { invoke, invokeAsync } from "../utils/FunctionWrappers";
|
|
50
|
+
import { generateCredentialKey } from "../cache/utils/CacheHelpers";
|
|
51
|
+
|
|
52
|
+
const DEFAULT_REFRESH_TOKEN_EXPIRATION_OFFSET_SECONDS = 300; // 5 Minutes
|
|
53
|
+
|
|
50
54
|
/**
|
|
51
55
|
* OAuth2.0 refresh token client
|
|
52
56
|
* @internal
|
|
@@ -102,8 +106,7 @@ export class RefreshTokenClient extends BaseClient {
|
|
|
102
106
|
undefined,
|
|
103
107
|
true,
|
|
104
108
|
request.forceCache,
|
|
105
|
-
requestId
|
|
106
|
-
request.expiresOn
|
|
109
|
+
requestId
|
|
107
110
|
);
|
|
108
111
|
}
|
|
109
112
|
|
|
@@ -141,7 +144,7 @@ export class RefreshTokenClient extends BaseClient {
|
|
|
141
144
|
// if the app is part of the family, retrive a Family refresh token if present and make a refreshTokenRequest
|
|
142
145
|
if (isFOCI) {
|
|
143
146
|
try {
|
|
144
|
-
return invokeAsync(
|
|
147
|
+
return await invokeAsync(
|
|
145
148
|
this.acquireTokenWithCachedRefreshToken.bind(this),
|
|
146
149
|
PerformanceEvents.RefreshTokenClientAcquireTokenWithCachedRefreshToken,
|
|
147
150
|
this.logger,
|
|
@@ -218,9 +221,12 @@ export class RefreshTokenClient extends BaseClient {
|
|
|
218
221
|
}
|
|
219
222
|
|
|
220
223
|
if (
|
|
221
|
-
request.enableRTExpiryCheck &&
|
|
222
224
|
refreshToken.expiresOn &&
|
|
223
|
-
|
|
225
|
+
TimeUtils.isTokenExpired(
|
|
226
|
+
refreshToken.expiresOn,
|
|
227
|
+
request.refreshTokenExpirationOffsetSeconds ||
|
|
228
|
+
DEFAULT_REFRESH_TOKEN_EXPIRATION_OFFSET_SECONDS
|
|
229
|
+
)
|
|
224
230
|
) {
|
|
225
231
|
throw createInteractionRequiredAuthError(
|
|
226
232
|
InteractionRequiredAuthErrorCodes.refreshTokenExpired
|
|
@@ -237,16 +243,31 @@ export class RefreshTokenClient extends BaseClient {
|
|
|
237
243
|
credential: request.account.homeAccountId,
|
|
238
244
|
type: CcsCredentialType.HOME_ACCOUNT_ID,
|
|
239
245
|
},
|
|
240
|
-
expiresOn: refreshToken.expiresOn,
|
|
241
246
|
};
|
|
242
247
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
try {
|
|
249
|
+
return await invokeAsync(
|
|
250
|
+
this.acquireToken.bind(this),
|
|
251
|
+
PerformanceEvents.RefreshTokenClientAcquireToken,
|
|
252
|
+
this.logger,
|
|
253
|
+
this.performanceClient,
|
|
254
|
+
request.correlationId
|
|
255
|
+
)(refreshTokenRequest);
|
|
256
|
+
} catch (e) {
|
|
257
|
+
if (
|
|
258
|
+
e instanceof InteractionRequiredAuthError &&
|
|
259
|
+
e.subError === InteractionRequiredAuthErrorCodes.badToken
|
|
260
|
+
) {
|
|
261
|
+
// Remove bad refresh token from cache
|
|
262
|
+
this.logger.verbose(
|
|
263
|
+
"acquireTokenWithRefreshToken: bad refresh token, removing from cache"
|
|
264
|
+
);
|
|
265
|
+
const badRefreshTokenKey = generateCredentialKey(refreshToken);
|
|
266
|
+
this.cacheManager.removeRefreshToken(badRefreshTokenKey);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
throw e;
|
|
270
|
+
}
|
|
250
271
|
}
|
|
251
272
|
|
|
252
273
|
/**
|
|
@@ -23,6 +23,7 @@ import { checkMaxAge, extractTokenClaims } from "../account/AuthToken";
|
|
|
23
23
|
import { TokenClaims } from "../account/TokenClaims";
|
|
24
24
|
import { PerformanceEvents } from "../telemetry/performance/PerformanceEvent";
|
|
25
25
|
import { invokeAsync } from "../utils/FunctionWrappers";
|
|
26
|
+
import { getTenantFromAuthorityString } from "../authority/Authority";
|
|
26
27
|
|
|
27
28
|
/** @internal */
|
|
28
29
|
export class SilentFlowClient extends BaseClient {
|
|
@@ -118,18 +119,20 @@ export class SilentFlowClient extends BaseClient {
|
|
|
118
119
|
);
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
const
|
|
122
|
-
request.
|
|
123
|
-
|
|
124
|
-
const
|
|
122
|
+
const requestTenantId =
|
|
123
|
+
request.account.tenantId ||
|
|
124
|
+
getTenantFromAuthorityString(request.authority);
|
|
125
|
+
const tokenKeys = this.cacheManager.getTokenKeys();
|
|
126
|
+
const cachedAccessToken = this.cacheManager.getAccessToken(
|
|
125
127
|
request.account,
|
|
126
128
|
request,
|
|
127
|
-
|
|
129
|
+
tokenKeys,
|
|
130
|
+
requestTenantId,
|
|
128
131
|
this.performanceClient,
|
|
129
132
|
request.correlationId
|
|
130
133
|
);
|
|
131
134
|
|
|
132
|
-
if (!
|
|
135
|
+
if (!cachedAccessToken) {
|
|
133
136
|
// must refresh due to non-existent access_token
|
|
134
137
|
this.setCacheOutcome(
|
|
135
138
|
CacheOutcome.NO_CACHED_ACCESS_TOKEN,
|
|
@@ -139,9 +142,9 @@ export class SilentFlowClient extends BaseClient {
|
|
|
139
142
|
ClientAuthErrorCodes.tokenRefreshRequired
|
|
140
143
|
);
|
|
141
144
|
} else if (
|
|
142
|
-
TimeUtils.wasClockTurnedBack(
|
|
145
|
+
TimeUtils.wasClockTurnedBack(cachedAccessToken.cachedAt) ||
|
|
143
146
|
TimeUtils.isTokenExpired(
|
|
144
|
-
|
|
147
|
+
cachedAccessToken.expiresOn,
|
|
145
148
|
this.config.systemOptions.tokenRenewalOffsetSeconds
|
|
146
149
|
)
|
|
147
150
|
) {
|
|
@@ -154,8 +157,8 @@ export class SilentFlowClient extends BaseClient {
|
|
|
154
157
|
ClientAuthErrorCodes.tokenRefreshRequired
|
|
155
158
|
);
|
|
156
159
|
} else if (
|
|
157
|
-
|
|
158
|
-
TimeUtils.isTokenExpired(
|
|
160
|
+
cachedAccessToken.refreshOn &&
|
|
161
|
+
TimeUtils.isTokenExpired(cachedAccessToken.refreshOn, 0)
|
|
159
162
|
) {
|
|
160
163
|
// must refresh (in the background) due to the refresh_in value
|
|
161
164
|
lastCacheOutcome = CacheOutcome.PROACTIVELY_REFRESHED;
|
|
@@ -163,6 +166,23 @@ export class SilentFlowClient extends BaseClient {
|
|
|
163
166
|
// don't throw ClientAuthError.createRefreshRequiredError(), return cached token instead
|
|
164
167
|
}
|
|
165
168
|
|
|
169
|
+
const environment =
|
|
170
|
+
request.authority || this.authority.getPreferredCache();
|
|
171
|
+
const cacheRecord: CacheRecord = {
|
|
172
|
+
account: this.cacheManager.readAccountFromCache(request.account),
|
|
173
|
+
accessToken: cachedAccessToken,
|
|
174
|
+
idToken: this.cacheManager.getIdToken(
|
|
175
|
+
request.account,
|
|
176
|
+
tokenKeys,
|
|
177
|
+
requestTenantId,
|
|
178
|
+
this.performanceClient,
|
|
179
|
+
request.correlationId
|
|
180
|
+
),
|
|
181
|
+
refreshToken: null,
|
|
182
|
+
appMetadata:
|
|
183
|
+
this.cacheManager.readAppMetadataFromCache(environment),
|
|
184
|
+
};
|
|
185
|
+
|
|
166
186
|
this.setCacheOutcome(lastCacheOutcome, request.correlationId);
|
|
167
187
|
|
|
168
188
|
if (this.config.serverTelemetryManager) {
|
|
@@ -231,7 +251,7 @@ export class SilentFlowClient extends BaseClient {
|
|
|
231
251
|
checkMaxAge(authTime, request.maxAge);
|
|
232
252
|
}
|
|
233
253
|
|
|
234
|
-
return
|
|
254
|
+
return ResponseHandler.generateAuthenticationResult(
|
|
235
255
|
this.cryptoUtils,
|
|
236
256
|
this.authority,
|
|
237
257
|
cacheRecord,
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
import { INetworkModule } from "../network/INetworkModule";
|
|
7
7
|
import { DEFAULT_CRYPTO_IMPLEMENTATION, ICrypto } from "../crypto/ICrypto";
|
|
8
8
|
import { ILoggerCallback, Logger, LogLevel } from "../logger/Logger";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Constants,
|
|
11
|
+
DEFAULT_TOKEN_RENEWAL_OFFSET_SEC,
|
|
12
|
+
} from "../utils/Constants";
|
|
10
13
|
import { version } from "../packageMetadata";
|
|
11
14
|
import { Authority } from "../authority/Authority";
|
|
12
15
|
import { AzureCloudInstance } from "../authority/AuthorityOptions";
|
|
@@ -21,9 +24,6 @@ import {
|
|
|
21
24
|
createClientAuthError,
|
|
22
25
|
} from "../error/ClientAuthError";
|
|
23
26
|
|
|
24
|
-
// Token renewal offset default in seconds
|
|
25
|
-
const DEFAULT_TOKEN_RENEWAL_OFFSET_SEC = 300;
|
|
26
|
-
|
|
27
27
|
/**
|
|
28
28
|
* Use the configuration object to configure MSAL Modules and initialize the base interfaces for MSAL.
|
|
29
29
|
*
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const CLIENT_ID = "client_id";
|
|
7
|
+
export const REDIRECT_URI = "redirect_uri";
|
|
8
|
+
export const RESPONSE_TYPE = "response_type";
|
|
9
|
+
export const RESPONSE_MODE = "response_mode";
|
|
10
|
+
export const GRANT_TYPE = "grant_type";
|
|
11
|
+
export const CLAIMS = "claims";
|
|
12
|
+
export const SCOPE = "scope";
|
|
13
|
+
export const ERROR = "error";
|
|
14
|
+
export const ERROR_DESCRIPTION = "error_description";
|
|
15
|
+
export const ACCESS_TOKEN = "access_token";
|
|
16
|
+
export const ID_TOKEN = "id_token";
|
|
17
|
+
export const REFRESH_TOKEN = "refresh_token";
|
|
18
|
+
export const EXPIRES_IN = "expires_in";
|
|
19
|
+
export const REFRESH_TOKEN_EXPIRES_IN = "refresh_token_expires_in";
|
|
20
|
+
export const STATE = "state";
|
|
21
|
+
export const NONCE = "nonce";
|
|
22
|
+
export const PROMPT = "prompt";
|
|
23
|
+
export const SESSION_STATE = "session_state";
|
|
24
|
+
export const CLIENT_INFO = "client_info";
|
|
25
|
+
export const CODE = "code";
|
|
26
|
+
export const CODE_CHALLENGE = "code_challenge";
|
|
27
|
+
export const CODE_CHALLENGE_METHOD = "code_challenge_method";
|
|
28
|
+
export const CODE_VERIFIER = "code_verifier";
|
|
29
|
+
export const CLIENT_REQUEST_ID = "client-request-id";
|
|
30
|
+
export const X_CLIENT_SKU = "x-client-SKU";
|
|
31
|
+
export const X_CLIENT_VER = "x-client-VER";
|
|
32
|
+
export const X_CLIENT_OS = "x-client-OS";
|
|
33
|
+
export const X_CLIENT_CPU = "x-client-CPU";
|
|
34
|
+
export const X_CLIENT_CURR_TELEM = "x-client-current-telemetry";
|
|
35
|
+
export const X_CLIENT_LAST_TELEM = "x-client-last-telemetry";
|
|
36
|
+
export const X_MS_LIB_CAPABILITY = "x-ms-lib-capability";
|
|
37
|
+
export const X_APP_NAME = "x-app-name";
|
|
38
|
+
export const X_APP_VER = "x-app-ver";
|
|
39
|
+
export const POST_LOGOUT_URI = "post_logout_redirect_uri";
|
|
40
|
+
export const ID_TOKEN_HINT = "id_token_hint";
|
|
41
|
+
export const DEVICE_CODE = "device_code";
|
|
42
|
+
export const CLIENT_SECRET = "client_secret";
|
|
43
|
+
export const CLIENT_ASSERTION = "client_assertion";
|
|
44
|
+
export const CLIENT_ASSERTION_TYPE = "client_assertion_type";
|
|
45
|
+
export const TOKEN_TYPE = "token_type";
|
|
46
|
+
export const REQ_CNF = "req_cnf";
|
|
47
|
+
export const OBO_ASSERTION = "assertion";
|
|
48
|
+
export const REQUESTED_TOKEN_USE = "requested_token_use";
|
|
49
|
+
export const ON_BEHALF_OF = "on_behalf_of";
|
|
50
|
+
export const FOCI = "foci";
|
|
51
|
+
export const CCS_HEADER = "X-AnchorMailbox";
|
|
52
|
+
export const RETURN_SPA_CODE = "return_spa_code";
|
|
53
|
+
export const NATIVE_BROKER = "nativebroker";
|
|
54
|
+
export const LOGOUT_HINT = "logout_hint";
|
|
55
|
+
export const SID = "sid";
|
|
56
|
+
export const LOGIN_HINT = "login_hint";
|
|
57
|
+
export const DOMAIN_HINT = "domain_hint";
|
|
@@ -140,7 +140,7 @@ export class PopTokenGenerator {
|
|
|
140
140
|
? new UrlString(resourceRequestUri)
|
|
141
141
|
: undefined;
|
|
142
142
|
const resourceUrlComponents = resourceUrlString?.getUrlComponents();
|
|
143
|
-
return
|
|
143
|
+
return this.cryptoUtils.signJwt(
|
|
144
144
|
{
|
|
145
145
|
at: payload,
|
|
146
146
|
ts: TimeUtils.nowSeconds(),
|