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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dist/account/AccountInfo.d.ts +31 -31
  2. package/dist/account/AuthToken.d.ts +17 -17
  3. package/dist/account/AuthToken.mjs +58 -58
  4. package/dist/account/CcsCredential.d.ts +9 -9
  5. package/dist/account/CcsCredential.mjs +8 -8
  6. package/dist/account/ClientCredentials.d.ts +14 -14
  7. package/dist/account/ClientInfo.d.ts +19 -19
  8. package/dist/account/ClientInfo.mjs +37 -37
  9. package/dist/account/TokenClaims.d.ts +65 -65
  10. package/dist/authority/Authority.d.ts +249 -249
  11. package/dist/authority/Authority.mjs +788 -788
  12. package/dist/authority/AuthorityFactory.d.ts +31 -31
  13. package/dist/authority/AuthorityFactory.mjs +47 -47
  14. package/dist/authority/AuthorityMetadata.d.ts +503 -503
  15. package/dist/authority/AuthorityMetadata.mjs +603 -603
  16. package/dist/authority/AuthorityOptions.d.ts +27 -27
  17. package/dist/authority/AuthorityOptions.mjs +18 -18
  18. package/dist/authority/AuthorityType.d.ts +10 -10
  19. package/dist/authority/AuthorityType.mjs +13 -13
  20. package/dist/authority/AzureRegion.d.ts +1 -1
  21. package/dist/authority/AzureRegionConfiguration.d.ts +5 -5
  22. package/dist/authority/CloudDiscoveryMetadata.d.ts +5 -5
  23. package/dist/authority/CloudInstanceDiscoveryErrorResponse.d.ts +13 -13
  24. package/dist/authority/CloudInstanceDiscoveryErrorResponse.mjs +8 -8
  25. package/dist/authority/CloudInstanceDiscoveryResponse.d.ts +9 -9
  26. package/dist/authority/CloudInstanceDiscoveryResponse.mjs +8 -8
  27. package/dist/authority/ImdsOptions.d.ts +5 -5
  28. package/dist/authority/OIDCOptions.d.ts +8 -8
  29. package/dist/authority/OpenIdConfigResponse.d.ts +11 -11
  30. package/dist/authority/OpenIdConfigResponse.mjs +10 -10
  31. package/dist/authority/ProtocolMode.d.ts +8 -8
  32. package/dist/authority/ProtocolMode.mjs +11 -11
  33. package/dist/authority/RegionDiscovery.d.ts +32 -32
  34. package/dist/authority/RegionDiscovery.mjs +106 -106
  35. package/dist/authority/RegionDiscoveryMetadata.d.ts +6 -6
  36. package/dist/broker/nativeBroker/INativeBrokerPlugin.d.ts +15 -15
  37. package/dist/cache/CacheManager.d.ts +485 -485
  38. package/dist/cache/CacheManager.mjs +1102 -1102
  39. package/dist/cache/entities/AccessTokenEntity.d.ts +25 -25
  40. package/dist/cache/entities/AccountEntity.d.ts +101 -101
  41. package/dist/cache/entities/AccountEntity.mjs +218 -218
  42. package/dist/cache/entities/AppMetadataEntity.d.ts +39 -39
  43. package/dist/cache/entities/AppMetadataEntity.mjs +66 -66
  44. package/dist/cache/entities/AuthorityMetadataEntity.d.ts +48 -48
  45. package/dist/cache/entities/AuthorityMetadataEntity.mjs +78 -78
  46. package/dist/cache/entities/CacheRecord.d.ts +14 -14
  47. package/dist/cache/entities/CacheRecord.mjs +14 -14
  48. package/dist/cache/entities/CredentialEntity.d.ts +30 -30
  49. package/dist/cache/entities/IdTokenEntity.d.ts +8 -8
  50. package/dist/cache/entities/RefreshTokenEntity.d.ts +7 -5
  51. package/dist/cache/entities/RefreshTokenEntity.d.ts.map +1 -1
  52. package/dist/cache/entities/ServerTelemetryEntity.d.ts +5 -5
  53. package/dist/cache/entities/ThrottlingEntity.d.ts +13 -13
  54. package/dist/cache/entities/ThrottlingEntity.mjs +23 -23
  55. package/dist/cache/interface/ICacheManager.d.ts +166 -166
  56. package/dist/cache/interface/ICachePlugin.d.ts +5 -5
  57. package/dist/cache/interface/ISerializableTokenCache.d.ts +4 -4
  58. package/dist/cache/persistence/TokenCacheContext.d.ts +23 -23
  59. package/dist/cache/persistence/TokenCacheContext.mjs +25 -25
  60. package/dist/cache/utils/CacheHelpers.d.ts +64 -64
  61. package/dist/cache/utils/CacheHelpers.d.ts.map +1 -1
  62. package/dist/cache/utils/CacheHelpers.mjs +234 -231
  63. package/dist/cache/utils/CacheHelpers.mjs.map +1 -1
  64. package/dist/cache/utils/CacheTypes.d.ts +67 -67
  65. package/dist/client/AuthorizationCodeClient.d.ts +74 -74
  66. package/dist/client/AuthorizationCodeClient.d.ts.map +1 -1
  67. package/dist/client/AuthorizationCodeClient.mjs +402 -402
  68. package/dist/client/AuthorizationCodeClient.mjs.map +1 -1
  69. package/dist/client/BaseClient.d.ts +51 -51
  70. package/dist/client/BaseClient.mjs +100 -100
  71. package/dist/client/RefreshTokenClient.d.ts +35 -35
  72. package/dist/client/RefreshTokenClient.d.ts.map +1 -1
  73. package/dist/client/RefreshTokenClient.mjs +193 -187
  74. package/dist/client/RefreshTokenClient.mjs.map +1 -1
  75. package/dist/client/SilentFlowClient.d.ts +27 -27
  76. package/dist/client/SilentFlowClient.mjs +118 -118
  77. package/dist/config/AppTokenProvider.d.ts +38 -38
  78. package/dist/config/ClientConfiguration.d.ts +150 -150
  79. package/dist/config/ClientConfiguration.mjs +97 -97
  80. package/dist/crypto/ICrypto.d.ts +58 -58
  81. package/dist/crypto/ICrypto.mjs +30 -30
  82. package/dist/crypto/IGuidGenerator.d.ts +4 -4
  83. package/dist/crypto/JoseHeader.d.ts +22 -22
  84. package/dist/crypto/JoseHeader.mjs +37 -37
  85. package/dist/crypto/PopTokenGenerator.d.ts +60 -60
  86. package/dist/crypto/PopTokenGenerator.mjs +82 -82
  87. package/dist/crypto/SignedHttpRequest.d.ts +15 -15
  88. package/dist/error/AuthError.d.ts +44 -44
  89. package/dist/error/AuthError.mjs +46 -46
  90. package/dist/error/AuthErrorCodes.d.ts +5 -5
  91. package/dist/error/AuthErrorCodes.mjs +9 -9
  92. package/dist/error/ClientAuthError.d.ts +237 -237
  93. package/dist/error/ClientAuthError.mjs +249 -249
  94. package/dist/error/ClientAuthErrorCodes.d.ts +44 -44
  95. package/dist/error/ClientAuthErrorCodes.mjs +48 -48
  96. package/dist/error/ClientConfigurationError.d.ts +128 -128
  97. package/dist/error/ClientConfigurationError.mjs +135 -135
  98. package/dist/error/ClientConfigurationErrorCodes.d.ts +22 -22
  99. package/dist/error/ClientConfigurationErrorCodes.mjs +26 -26
  100. package/dist/error/InteractionRequiredAuthError.d.ts +57 -57
  101. package/dist/error/InteractionRequiredAuthError.d.ts.map +1 -1
  102. package/dist/error/InteractionRequiredAuthError.mjs +78 -77
  103. package/dist/error/InteractionRequiredAuthError.mjs.map +1 -1
  104. package/dist/error/InteractionRequiredAuthErrorCodes.d.ts +6 -5
  105. package/dist/error/InteractionRequiredAuthErrorCodes.d.ts.map +1 -1
  106. package/dist/error/InteractionRequiredAuthErrorCodes.mjs +13 -12
  107. package/dist/error/InteractionRequiredAuthErrorCodes.mjs.map +1 -1
  108. package/dist/error/JoseHeaderError.d.ts +15 -15
  109. package/dist/error/JoseHeaderError.mjs +22 -22
  110. package/dist/error/JoseHeaderErrorCodes.d.ts +2 -2
  111. package/dist/error/JoseHeaderErrorCodes.mjs +6 -6
  112. package/dist/error/ServerError.d.ts +7 -7
  113. package/dist/error/ServerError.mjs +14 -14
  114. package/dist/index.cjs +8290 -8278
  115. package/dist/index.cjs.map +1 -1
  116. package/dist/index.d.ts +98 -98
  117. package/dist/index.mjs +1 -1
  118. package/dist/logger/Logger.d.ts +95 -95
  119. package/dist/logger/Logger.mjs +188 -188
  120. package/dist/network/INetworkModule.d.ts +29 -29
  121. package/dist/network/INetworkModule.mjs +12 -12
  122. package/dist/network/NetworkManager.d.ts +33 -33
  123. package/dist/network/NetworkManager.mjs +34 -34
  124. package/dist/network/RequestThumbprint.d.ts +18 -18
  125. package/dist/network/ThrottlingUtils.d.ts +42 -42
  126. package/dist/network/ThrottlingUtils.mjs +95 -95
  127. package/dist/packageMetadata.d.ts +2 -2
  128. package/dist/packageMetadata.d.ts.map +1 -1
  129. package/dist/packageMetadata.mjs +4 -4
  130. package/dist/packageMetadata.mjs.map +1 -1
  131. package/dist/request/AuthenticationHeaderParser.d.ts +19 -19
  132. package/dist/request/AuthenticationHeaderParser.mjs +55 -55
  133. package/dist/request/BaseAuthRequest.d.ts +43 -43
  134. package/dist/request/CommonAuthorizationCodeRequest.d.ts +27 -27
  135. package/dist/request/CommonAuthorizationUrlRequest.d.ts +50 -50
  136. package/dist/request/CommonClientCredentialRequest.d.ts +17 -17
  137. package/dist/request/CommonDeviceCodeRequest.d.ts +21 -21
  138. package/dist/request/CommonEndSessionRequest.d.ts +21 -21
  139. package/dist/request/CommonOnBehalfOfRequest.d.ts +13 -13
  140. package/dist/request/CommonRefreshTokenRequest.d.ts +23 -22
  141. package/dist/request/CommonRefreshTokenRequest.d.ts.map +1 -1
  142. package/dist/request/CommonSilentFlowRequest.d.ts +21 -20
  143. package/dist/request/CommonSilentFlowRequest.d.ts.map +1 -1
  144. package/dist/request/CommonUsernamePasswordRequest.d.ts +17 -17
  145. package/dist/request/NativeRequest.d.ts +19 -19
  146. package/dist/request/NativeSignOutRequest.d.ts +5 -5
  147. package/dist/request/RequestParameterBuilder.d.ts +217 -217
  148. package/dist/request/RequestParameterBuilder.mjs +381 -381
  149. package/dist/request/RequestValidator.d.ts +33 -33
  150. package/dist/request/RequestValidator.mjs +81 -81
  151. package/dist/request/ScopeSet.d.ts +88 -88
  152. package/dist/request/ScopeSet.mjs +197 -197
  153. package/dist/request/StoreInCache.d.ts +8 -8
  154. package/dist/response/AuthenticationResult.d.ts +41 -41
  155. package/dist/response/AuthorizationCodePayload.d.ts +13 -13
  156. package/dist/response/DeviceCodeResponse.d.ts +25 -25
  157. package/dist/response/ExternalTokenResponse.d.ts +15 -15
  158. package/dist/response/IMDSBadResponse.d.ts +4 -4
  159. package/dist/response/ResponseHandler.d.ts +67 -67
  160. package/dist/response/ResponseHandler.d.ts.map +1 -1
  161. package/dist/response/ResponseHandler.mjs +312 -312
  162. package/dist/response/ResponseHandler.mjs.map +1 -1
  163. package/dist/response/ServerAuthorizationCodeResponse.d.ts +25 -25
  164. package/dist/response/ServerAuthorizationTokenResponse.d.ts +46 -46
  165. package/dist/telemetry/performance/IPerformanceClient.d.ts +51 -51
  166. package/dist/telemetry/performance/IPerformanceMeasurement.d.ts +5 -5
  167. package/dist/telemetry/performance/PerformanceClient.d.ts +191 -191
  168. package/dist/telemetry/performance/PerformanceClient.mjs +409 -409
  169. package/dist/telemetry/performance/PerformanceEvent.d.ts +464 -464
  170. package/dist/telemetry/performance/PerformanceEvent.mjs +253 -253
  171. package/dist/telemetry/performance/StubPerformanceClient.d.ts +24 -24
  172. package/dist/telemetry/performance/StubPerformanceClient.mjs +76 -76
  173. package/dist/telemetry/server/ServerTelemetryManager.d.ts +66 -66
  174. package/dist/telemetry/server/ServerTelemetryManager.mjs +201 -201
  175. package/dist/telemetry/server/ServerTelemetryRequest.d.ts +8 -8
  176. package/dist/url/IUri.d.ts +12 -12
  177. package/dist/url/UrlString.d.ts +48 -48
  178. package/dist/url/UrlString.mjs +161 -161
  179. package/dist/utils/Constants.d.ts +365 -365
  180. package/dist/utils/Constants.mjs +375 -375
  181. package/dist/utils/FunctionWrappers.d.ts +27 -27
  182. package/dist/utils/FunctionWrappers.mjs +84 -84
  183. package/dist/utils/MsalTypes.d.ts +6 -6
  184. package/dist/utils/ProtocolUtils.d.ts +42 -42
  185. package/dist/utils/ProtocolUtils.mjs +69 -69
  186. package/dist/utils/StringUtils.d.ts +40 -40
  187. package/dist/utils/StringUtils.mjs +95 -95
  188. package/dist/utils/TimeUtils.d.ts +27 -27
  189. package/dist/utils/TimeUtils.mjs +45 -45
  190. package/dist/utils/UrlUtils.d.ts +10 -10
  191. package/dist/utils/UrlUtils.mjs +44 -44
  192. package/package.json +92 -92
  193. package/src/cache/entities/RefreshTokenEntity.ts +3 -1
  194. package/src/cache/utils/CacheHelpers.ts +6 -1
  195. package/src/client/AuthorizationCodeClient.ts +2 -1
  196. package/src/client/RefreshTokenClient.ts +13 -1
  197. package/src/error/InteractionRequiredAuthError.ts +2 -0
  198. package/src/error/InteractionRequiredAuthErrorCodes.ts +1 -0
  199. package/src/packageMetadata.ts +1 -1
  200. package/src/request/CommonRefreshTokenRequest.ts +1 -0
  201. package/src/request/CommonSilentFlowRequest.ts +1 -0
  202. package/src/response/ResponseHandler.ts +8 -4
@@ -1,4 +1,4 @@
1
- /*! @azure/msal-common v14.4.0 2023-11-07 */
1
+ /*! @azure/msal-common v14.4.1-beta.1 2023-11-14 */
2
2
  'use strict';
3
3
  import { createClientAuthError } from '../error/ClientAuthError.mjs';
4
4
  import { ServerError } from '../error/ServerError.mjs';
@@ -16,317 +16,317 @@ import { extractTokenClaims, checkMaxAge } from '../account/AuthToken.mjs';
16
16
  import { createAccessTokenEntity, createRefreshTokenEntity, createIdTokenEntity } from '../cache/utils/CacheHelpers.mjs';
17
17
  import { stateNotFound, invalidState, stateMismatch, nonceMismatch, authTimeNotFound, invalidCacheEnvironment, keyIdMissing } from '../error/ClientAuthErrorCodes.mjs';
18
18
 
19
- /*
20
- * Copyright (c) Microsoft Corporation. All rights reserved.
21
- * Licensed under the MIT License.
22
- */
23
- /**
24
- * Class that handles response parsing.
25
- * @internal
26
- */
27
- class ResponseHandler {
28
- constructor(clientId, cacheStorage, cryptoObj, logger, serializableCache, persistencePlugin, performanceClient) {
29
- this.clientId = clientId;
30
- this.cacheStorage = cacheStorage;
31
- this.cryptoObj = cryptoObj;
32
- this.logger = logger;
33
- this.serializableCache = serializableCache;
34
- this.persistencePlugin = persistencePlugin;
35
- this.performanceClient = performanceClient;
36
- }
37
- /**
38
- * Function which validates server authorization code response.
39
- * @param serverResponseHash
40
- * @param requestState
41
- * @param cryptoObj
42
- */
43
- validateServerAuthorizationCodeResponse(serverResponse, requestState) {
44
- if (!serverResponse.state || !requestState) {
45
- throw serverResponse.state
46
- ? createClientAuthError(stateNotFound, "Cached State")
47
- : createClientAuthError(stateNotFound, "Server State");
48
- }
49
- let decodedServerResponseState;
50
- let decodedRequestState;
51
- try {
52
- decodedServerResponseState = decodeURIComponent(serverResponse.state);
53
- }
54
- catch (e) {
55
- throw createClientAuthError(invalidState, serverResponse.state);
56
- }
57
- try {
58
- decodedRequestState = decodeURIComponent(requestState);
59
- }
60
- catch (e) {
61
- throw createClientAuthError(invalidState, serverResponse.state);
62
- }
63
- if (decodedServerResponseState !== decodedRequestState) {
64
- throw createClientAuthError(stateMismatch);
65
- }
66
- // Check for error
67
- if (serverResponse.error ||
68
- serverResponse.error_description ||
69
- serverResponse.suberror) {
70
- if (isInteractionRequiredError(serverResponse.error, serverResponse.error_description, serverResponse.suberror)) {
71
- throw new InteractionRequiredAuthError(serverResponse.error || "", serverResponse.error_description, serverResponse.suberror, serverResponse.timestamp || "", serverResponse.trace_id || "", serverResponse.correlation_id || "", serverResponse.claims || "");
72
- }
73
- throw new ServerError(serverResponse.error || "", serverResponse.error_description, serverResponse.suberror);
74
- }
75
- }
76
- /**
77
- * Function which validates server authorization token response.
78
- * @param serverResponse
79
- * @param refreshAccessToken
80
- */
81
- validateTokenResponse(serverResponse, refreshAccessToken) {
82
- // Check for error
83
- if (serverResponse.error ||
84
- serverResponse.error_description ||
85
- serverResponse.suberror) {
86
- const errString = `${serverResponse.error_codes} - [${serverResponse.timestamp}]: ${serverResponse.error_description} - Correlation ID: ${serverResponse.correlation_id} - Trace ID: ${serverResponse.trace_id}`;
87
- const serverError = new ServerError(serverResponse.error, errString, serverResponse.suberror);
88
- // check if 500 error
89
- if (refreshAccessToken &&
90
- serverResponse.status &&
91
- serverResponse.status >= HttpStatus.SERVER_ERROR_RANGE_START &&
92
- serverResponse.status <= HttpStatus.SERVER_ERROR_RANGE_END) {
93
- this.logger.warning(`executeTokenRequest:validateTokenResponse - AAD is currently unavailable and the access token is unable to be refreshed.\n${serverError}`);
94
- // don't throw an exception, but alert the user via a log that the token was unable to be refreshed
95
- return;
96
- // check if 400 error
97
- }
98
- else if (refreshAccessToken &&
99
- serverResponse.status &&
100
- serverResponse.status >= HttpStatus.CLIENT_ERROR_RANGE_START &&
101
- serverResponse.status <= HttpStatus.CLIENT_ERROR_RANGE_END) {
102
- this.logger.warning(`executeTokenRequest:validateTokenResponse - AAD is currently available but is unable to refresh the access token.\n${serverError}`);
103
- // don't throw an exception, but alert the user via a log that the token was unable to be refreshed
104
- return;
105
- }
106
- if (isInteractionRequiredError(serverResponse.error, serverResponse.error_description, serverResponse.suberror)) {
107
- throw new InteractionRequiredAuthError(serverResponse.error, serverResponse.error_description, serverResponse.suberror, serverResponse.timestamp || Constants.EMPTY_STRING, serverResponse.trace_id || Constants.EMPTY_STRING, serverResponse.correlation_id || Constants.EMPTY_STRING, serverResponse.claims || Constants.EMPTY_STRING);
108
- }
109
- throw serverError;
110
- }
111
- }
112
- /**
113
- * Returns a constructed token response based on given string. Also manages the cache updates and cleanups.
114
- * @param serverTokenResponse
115
- * @param authority
116
- */
117
- async handleServerTokenResponse(serverTokenResponse, authority, reqTimestamp, request, authCodePayload, userAssertionHash, handlingRefreshTokenResponse, forceCacheRefreshTokenResponse, serverRequestId) {
118
- this.performanceClient?.addQueueMeasurement(PerformanceEvents.HandleServerTokenResponse, serverTokenResponse.correlation_id);
119
- // create an idToken object (not entity)
120
- let idTokenClaims;
121
- if (serverTokenResponse.id_token) {
122
- idTokenClaims = extractTokenClaims(serverTokenResponse.id_token || Constants.EMPTY_STRING, this.cryptoObj.base64Decode);
123
- // token nonce check (TODO: Add a warning if no nonce is given?)
124
- if (authCodePayload && authCodePayload.nonce) {
125
- if (idTokenClaims.nonce !== authCodePayload.nonce) {
126
- throw createClientAuthError(nonceMismatch);
127
- }
128
- }
129
- // token max_age check
130
- if (request.maxAge || request.maxAge === 0) {
131
- const authTime = idTokenClaims.auth_time;
132
- if (!authTime) {
133
- throw createClientAuthError(authTimeNotFound);
134
- }
135
- checkMaxAge(authTime, request.maxAge);
136
- }
137
- }
138
- // generate homeAccountId
139
- this.homeAccountIdentifier = AccountEntity.generateHomeAccountId(serverTokenResponse.client_info || Constants.EMPTY_STRING, authority.authorityType, this.logger, this.cryptoObj, idTokenClaims);
140
- // save the response tokens
141
- let requestStateObj;
142
- if (!!authCodePayload && !!authCodePayload.state) {
143
- requestStateObj = ProtocolUtils.parseRequestState(this.cryptoObj, authCodePayload.state);
144
- }
145
- // Add keyId from request to serverTokenResponse if defined
146
- serverTokenResponse.key_id =
147
- serverTokenResponse.key_id || request.sshKid || undefined;
148
- const cacheRecord = this.generateCacheRecord(serverTokenResponse, authority, reqTimestamp, request, idTokenClaims, userAssertionHash, authCodePayload);
149
- let cacheContext;
150
- try {
151
- if (this.persistencePlugin && this.serializableCache) {
152
- this.logger.verbose("Persistence enabled, calling beforeCacheAccess");
153
- cacheContext = new TokenCacheContext(this.serializableCache, true);
154
- await this.persistencePlugin.beforeCacheAccess(cacheContext);
155
- }
156
- /*
157
- * When saving a refreshed tokens to the cache, it is expected that the account that was used is present in the cache.
158
- * If not present, we should return null, as it's the case that another application called removeAccount in between
159
- * the calls to getAllAccounts and acquireTokenSilent. We should not overwrite that removal, unless explicitly flagged by
160
- * the developer, as in the case of refresh token flow used in ADAL Node to MSAL Node migration.
161
- */
162
- if (handlingRefreshTokenResponse &&
163
- !forceCacheRefreshTokenResponse &&
164
- cacheRecord.account) {
165
- const key = cacheRecord.account.generateAccountKey();
166
- const account = this.cacheStorage.getAccount(key);
167
- if (!account) {
168
- this.logger.warning("Account used to refresh tokens not in persistence, refreshed tokens will not be stored in the cache");
169
- return ResponseHandler.generateAuthenticationResult(this.cryptoObj, authority, cacheRecord, false, request, idTokenClaims, requestStateObj, undefined, serverRequestId);
170
- }
171
- }
172
- await this.cacheStorage.saveCacheRecord(cacheRecord, request.storeInCache);
173
- }
174
- finally {
175
- if (this.persistencePlugin &&
176
- this.serializableCache &&
177
- cacheContext) {
178
- this.logger.verbose("Persistence enabled, calling afterCacheAccess");
179
- await this.persistencePlugin.afterCacheAccess(cacheContext);
180
- }
181
- }
182
- return ResponseHandler.generateAuthenticationResult(this.cryptoObj, authority, cacheRecord, false, request, idTokenClaims, requestStateObj, serverTokenResponse, serverRequestId);
183
- }
184
- /**
185
- * Generates CacheRecord
186
- * @param serverTokenResponse
187
- * @param idTokenObj
188
- * @param authority
189
- */
190
- generateCacheRecord(serverTokenResponse, authority, reqTimestamp, request, idTokenClaims, userAssertionHash, authCodePayload) {
191
- const env = authority.getPreferredCache();
192
- if (!env) {
193
- throw createClientAuthError(invalidCacheEnvironment);
194
- }
195
- // IdToken: non AAD scenarios can have empty realm
196
- let cachedIdToken;
197
- let cachedAccount;
198
- if (serverTokenResponse.id_token && !!idTokenClaims) {
199
- cachedIdToken = createIdTokenEntity(this.homeAccountIdentifier, env, serverTokenResponse.id_token, this.clientId, idTokenClaims.tid || "");
200
- cachedAccount = AccountEntity.createAccount({
201
- homeAccountId: this.homeAccountIdentifier,
202
- idTokenClaims: idTokenClaims,
203
- clientInfo: serverTokenResponse.client_info,
204
- cloudGraphHostName: authCodePayload?.cloud_graph_host_name,
205
- msGraphHost: authCodePayload?.msgraph_host,
206
- }, authority);
207
- }
208
- // AccessToken
209
- let cachedAccessToken = null;
210
- if (serverTokenResponse.access_token) {
211
- // If scopes not returned in server response, use request scopes
212
- const responseScopes = serverTokenResponse.scope
213
- ? ScopeSet.fromString(serverTokenResponse.scope)
214
- : new ScopeSet(request.scopes || []);
215
- /*
216
- * Use timestamp calculated before request
217
- * Server may return timestamps as strings, parse to numbers if so.
218
- */
219
- const expiresIn = (typeof serverTokenResponse.expires_in === "string"
220
- ? parseInt(serverTokenResponse.expires_in, 10)
221
- : serverTokenResponse.expires_in) || 0;
222
- const extExpiresIn = (typeof serverTokenResponse.ext_expires_in === "string"
223
- ? parseInt(serverTokenResponse.ext_expires_in, 10)
224
- : serverTokenResponse.ext_expires_in) || 0;
225
- const refreshIn = (typeof serverTokenResponse.refresh_in === "string"
226
- ? parseInt(serverTokenResponse.refresh_in, 10)
227
- : serverTokenResponse.refresh_in) || undefined;
228
- const tokenExpirationSeconds = reqTimestamp + expiresIn;
229
- const extendedTokenExpirationSeconds = tokenExpirationSeconds + extExpiresIn;
230
- const refreshOnSeconds = refreshIn && refreshIn > 0
231
- ? reqTimestamp + refreshIn
232
- : undefined;
233
- // non AAD scenarios can have empty realm
234
- cachedAccessToken = createAccessTokenEntity(this.homeAccountIdentifier, env, serverTokenResponse.access_token, this.clientId, idTokenClaims?.tid || authority.tenant, responseScopes.printScopes(), tokenExpirationSeconds, extendedTokenExpirationSeconds, this.cryptoObj.base64Decode, refreshOnSeconds, serverTokenResponse.token_type, userAssertionHash, serverTokenResponse.key_id, request.claims, request.requestedClaimsHash);
235
- }
236
- // refreshToken
237
- let cachedRefreshToken = null;
238
- if (serverTokenResponse.refresh_token) {
239
- cachedRefreshToken = createRefreshTokenEntity(this.homeAccountIdentifier, env, serverTokenResponse.refresh_token, this.clientId, serverTokenResponse.foci, userAssertionHash);
240
- }
241
- // appMetadata
242
- let cachedAppMetadata = null;
243
- if (serverTokenResponse.foci) {
244
- cachedAppMetadata = AppMetadataEntity.createAppMetadataEntity(this.clientId, env, serverTokenResponse.foci);
245
- }
246
- return new CacheRecord(cachedAccount, cachedIdToken, cachedAccessToken, cachedRefreshToken, cachedAppMetadata);
247
- }
248
- /**
249
- * Creates an @AuthenticationResult from @CacheRecord , @IdToken , and a boolean that states whether or not the result is from cache.
250
- *
251
- * Optionally takes a state string that is set as-is in the response.
252
- *
253
- * @param cacheRecord
254
- * @param idTokenObj
255
- * @param fromTokenCache
256
- * @param stateString
257
- */
258
- static async generateAuthenticationResult(cryptoObj, authority, cacheRecord, fromTokenCache, request, idTokenClaims, requestState, serverTokenResponse, requestId) {
259
- let accessToken = Constants.EMPTY_STRING;
260
- let responseScopes = [];
261
- let expiresOn = null;
262
- let extExpiresOn;
263
- let refreshOn;
264
- let familyId = Constants.EMPTY_STRING;
265
- if (cacheRecord.accessToken) {
266
- if (cacheRecord.accessToken.tokenType === AuthenticationScheme.POP) {
267
- const popTokenGenerator = new PopTokenGenerator(cryptoObj);
268
- const { secret, keyId } = cacheRecord.accessToken;
269
- if (!keyId) {
270
- throw createClientAuthError(keyIdMissing);
271
- }
272
- accessToken = await popTokenGenerator.signPopToken(secret, keyId, request);
273
- }
274
- else {
275
- accessToken = cacheRecord.accessToken.secret;
276
- }
277
- responseScopes = ScopeSet.fromString(cacheRecord.accessToken.target).asArray();
278
- expiresOn = new Date(Number(cacheRecord.accessToken.expiresOn) * 1000);
279
- extExpiresOn = new Date(Number(cacheRecord.accessToken.extendedExpiresOn) * 1000);
280
- if (cacheRecord.accessToken.refreshOn) {
281
- refreshOn = new Date(Number(cacheRecord.accessToken.refreshOn) * 1000);
282
- }
283
- }
284
- if (cacheRecord.appMetadata) {
285
- familyId =
286
- cacheRecord.appMetadata.familyId === THE_FAMILY_ID
287
- ? THE_FAMILY_ID
288
- : "";
289
- }
290
- const uid = idTokenClaims?.oid || idTokenClaims?.sub || "";
291
- const tid = idTokenClaims?.tid || "";
292
- // for hybrid + native bridge enablement, send back the native account Id
293
- if (serverTokenResponse?.spa_accountid && !!cacheRecord.account) {
294
- cacheRecord.account.nativeAccountId =
295
- serverTokenResponse?.spa_accountid;
296
- }
297
- const accountInfo = cacheRecord.account
298
- ? {
299
- ...cacheRecord.account.getAccountInfo(),
300
- idTokenClaims,
301
- }
302
- : null;
303
- return {
304
- authority: authority.canonicalAuthority,
305
- uniqueId: uid,
306
- tenantId: tid,
307
- scopes: responseScopes,
308
- account: accountInfo,
309
- idToken: cacheRecord?.idToken?.secret || "",
310
- idTokenClaims: idTokenClaims || {},
311
- accessToken: accessToken,
312
- fromCache: fromTokenCache,
313
- expiresOn: expiresOn,
314
- extExpiresOn: extExpiresOn,
315
- refreshOn: refreshOn,
316
- correlationId: request.correlationId,
317
- requestId: requestId || Constants.EMPTY_STRING,
318
- familyId: familyId,
319
- tokenType: cacheRecord.accessToken?.tokenType || Constants.EMPTY_STRING,
320
- state: requestState
321
- ? requestState.userRequestState
322
- : Constants.EMPTY_STRING,
323
- cloudGraphHostName: cacheRecord.account?.cloudGraphHostName ||
324
- Constants.EMPTY_STRING,
325
- msGraphHost: cacheRecord.account?.msGraphHost || Constants.EMPTY_STRING,
326
- code: serverTokenResponse?.spa_code,
327
- fromNativeBroker: false,
328
- };
329
- }
19
+ /*
20
+ * Copyright (c) Microsoft Corporation. All rights reserved.
21
+ * Licensed under the MIT License.
22
+ */
23
+ /**
24
+ * Class that handles response parsing.
25
+ * @internal
26
+ */
27
+ class ResponseHandler {
28
+ constructor(clientId, cacheStorage, cryptoObj, logger, serializableCache, persistencePlugin, performanceClient) {
29
+ this.clientId = clientId;
30
+ this.cacheStorage = cacheStorage;
31
+ this.cryptoObj = cryptoObj;
32
+ this.logger = logger;
33
+ this.serializableCache = serializableCache;
34
+ this.persistencePlugin = persistencePlugin;
35
+ this.performanceClient = performanceClient;
36
+ }
37
+ /**
38
+ * Function which validates server authorization code response.
39
+ * @param serverResponseHash
40
+ * @param requestState
41
+ * @param cryptoObj
42
+ */
43
+ validateServerAuthorizationCodeResponse(serverResponse, requestState) {
44
+ if (!serverResponse.state || !requestState) {
45
+ throw serverResponse.state
46
+ ? createClientAuthError(stateNotFound, "Cached State")
47
+ : createClientAuthError(stateNotFound, "Server State");
48
+ }
49
+ let decodedServerResponseState;
50
+ let decodedRequestState;
51
+ try {
52
+ decodedServerResponseState = decodeURIComponent(serverResponse.state);
53
+ }
54
+ catch (e) {
55
+ throw createClientAuthError(invalidState, serverResponse.state);
56
+ }
57
+ try {
58
+ decodedRequestState = decodeURIComponent(requestState);
59
+ }
60
+ catch (e) {
61
+ throw createClientAuthError(invalidState, serverResponse.state);
62
+ }
63
+ if (decodedServerResponseState !== decodedRequestState) {
64
+ throw createClientAuthError(stateMismatch);
65
+ }
66
+ // Check for error
67
+ if (serverResponse.error ||
68
+ serverResponse.error_description ||
69
+ serverResponse.suberror) {
70
+ if (isInteractionRequiredError(serverResponse.error, serverResponse.error_description, serverResponse.suberror)) {
71
+ throw new InteractionRequiredAuthError(serverResponse.error || "", serverResponse.error_description, serverResponse.suberror, serverResponse.timestamp || "", serverResponse.trace_id || "", serverResponse.correlation_id || "", serverResponse.claims || "");
72
+ }
73
+ throw new ServerError(serverResponse.error || "", serverResponse.error_description, serverResponse.suberror);
74
+ }
75
+ }
76
+ /**
77
+ * Function which validates server authorization token response.
78
+ * @param serverResponse
79
+ * @param refreshAccessToken
80
+ */
81
+ validateTokenResponse(serverResponse, refreshAccessToken) {
82
+ // Check for error
83
+ if (serverResponse.error ||
84
+ serverResponse.error_description ||
85
+ serverResponse.suberror) {
86
+ const errString = `${serverResponse.error_codes} - [${serverResponse.timestamp}]: ${serverResponse.error_description} - Correlation ID: ${serverResponse.correlation_id} - Trace ID: ${serverResponse.trace_id}`;
87
+ const serverError = new ServerError(serverResponse.error, errString, serverResponse.suberror);
88
+ // check if 500 error
89
+ if (refreshAccessToken &&
90
+ serverResponse.status &&
91
+ serverResponse.status >= HttpStatus.SERVER_ERROR_RANGE_START &&
92
+ serverResponse.status <= HttpStatus.SERVER_ERROR_RANGE_END) {
93
+ this.logger.warning(`executeTokenRequest:validateTokenResponse - AAD is currently unavailable and the access token is unable to be refreshed.\n${serverError}`);
94
+ // don't throw an exception, but alert the user via a log that the token was unable to be refreshed
95
+ return;
96
+ // check if 400 error
97
+ }
98
+ else if (refreshAccessToken &&
99
+ serverResponse.status &&
100
+ serverResponse.status >= HttpStatus.CLIENT_ERROR_RANGE_START &&
101
+ serverResponse.status <= HttpStatus.CLIENT_ERROR_RANGE_END) {
102
+ this.logger.warning(`executeTokenRequest:validateTokenResponse - AAD is currently available but is unable to refresh the access token.\n${serverError}`);
103
+ // don't throw an exception, but alert the user via a log that the token was unable to be refreshed
104
+ return;
105
+ }
106
+ if (isInteractionRequiredError(serverResponse.error, serverResponse.error_description, serverResponse.suberror)) {
107
+ throw new InteractionRequiredAuthError(serverResponse.error, serverResponse.error_description, serverResponse.suberror, serverResponse.timestamp || Constants.EMPTY_STRING, serverResponse.trace_id || Constants.EMPTY_STRING, serverResponse.correlation_id || Constants.EMPTY_STRING, serverResponse.claims || Constants.EMPTY_STRING);
108
+ }
109
+ throw serverError;
110
+ }
111
+ }
112
+ /**
113
+ * Returns a constructed token response based on given string. Also manages the cache updates and cleanups.
114
+ * @param serverTokenResponse
115
+ * @param authority
116
+ */
117
+ async handleServerTokenResponse(serverTokenResponse, authority, reqTimestamp, request, authCodePayload, userAssertionHash, handlingRefreshTokenResponse, forceCacheRefreshTokenResponse, serverRequestId, rtExpiresOn) {
118
+ this.performanceClient?.addQueueMeasurement(PerformanceEvents.HandleServerTokenResponse, serverTokenResponse.correlation_id);
119
+ // create an idToken object (not entity)
120
+ let idTokenClaims;
121
+ if (serverTokenResponse.id_token) {
122
+ idTokenClaims = extractTokenClaims(serverTokenResponse.id_token || Constants.EMPTY_STRING, this.cryptoObj.base64Decode);
123
+ // token nonce check (TODO: Add a warning if no nonce is given?)
124
+ if (authCodePayload && authCodePayload.nonce) {
125
+ if (idTokenClaims.nonce !== authCodePayload.nonce) {
126
+ throw createClientAuthError(nonceMismatch);
127
+ }
128
+ }
129
+ // token max_age check
130
+ if (request.maxAge || request.maxAge === 0) {
131
+ const authTime = idTokenClaims.auth_time;
132
+ if (!authTime) {
133
+ throw createClientAuthError(authTimeNotFound);
134
+ }
135
+ checkMaxAge(authTime, request.maxAge);
136
+ }
137
+ }
138
+ // generate homeAccountId
139
+ this.homeAccountIdentifier = AccountEntity.generateHomeAccountId(serverTokenResponse.client_info || Constants.EMPTY_STRING, authority.authorityType, this.logger, this.cryptoObj, idTokenClaims);
140
+ // save the response tokens
141
+ let requestStateObj;
142
+ if (!!authCodePayload && !!authCodePayload.state) {
143
+ requestStateObj = ProtocolUtils.parseRequestState(this.cryptoObj, authCodePayload.state);
144
+ }
145
+ // Add keyId from request to serverTokenResponse if defined
146
+ serverTokenResponse.key_id =
147
+ serverTokenResponse.key_id || request.sshKid || undefined;
148
+ const cacheRecord = this.generateCacheRecord(serverTokenResponse, authority, reqTimestamp, request, idTokenClaims, userAssertionHash, authCodePayload, rtExpiresOn);
149
+ let cacheContext;
150
+ try {
151
+ if (this.persistencePlugin && this.serializableCache) {
152
+ this.logger.verbose("Persistence enabled, calling beforeCacheAccess");
153
+ cacheContext = new TokenCacheContext(this.serializableCache, true);
154
+ await this.persistencePlugin.beforeCacheAccess(cacheContext);
155
+ }
156
+ /*
157
+ * When saving a refreshed tokens to the cache, it is expected that the account that was used is present in the cache.
158
+ * If not present, we should return null, as it's the case that another application called removeAccount in between
159
+ * the calls to getAllAccounts and acquireTokenSilent. We should not overwrite that removal, unless explicitly flagged by
160
+ * the developer, as in the case of refresh token flow used in ADAL Node to MSAL Node migration.
161
+ */
162
+ if (handlingRefreshTokenResponse &&
163
+ !forceCacheRefreshTokenResponse &&
164
+ cacheRecord.account) {
165
+ const key = cacheRecord.account.generateAccountKey();
166
+ const account = this.cacheStorage.getAccount(key);
167
+ if (!account) {
168
+ this.logger.warning("Account used to refresh tokens not in persistence, refreshed tokens will not be stored in the cache");
169
+ return ResponseHandler.generateAuthenticationResult(this.cryptoObj, authority, cacheRecord, false, request, idTokenClaims, requestStateObj, undefined, serverRequestId);
170
+ }
171
+ }
172
+ await this.cacheStorage.saveCacheRecord(cacheRecord, request.storeInCache);
173
+ }
174
+ finally {
175
+ if (this.persistencePlugin &&
176
+ this.serializableCache &&
177
+ cacheContext) {
178
+ this.logger.verbose("Persistence enabled, calling afterCacheAccess");
179
+ await this.persistencePlugin.afterCacheAccess(cacheContext);
180
+ }
181
+ }
182
+ return ResponseHandler.generateAuthenticationResult(this.cryptoObj, authority, cacheRecord, false, request, idTokenClaims, requestStateObj, serverTokenResponse, serverRequestId);
183
+ }
184
+ /**
185
+ * Generates CacheRecord
186
+ * @param serverTokenResponse
187
+ * @param idTokenObj
188
+ * @param authority
189
+ */
190
+ generateCacheRecord(serverTokenResponse, authority, reqTimestamp, request, idTokenClaims, userAssertionHash, authCodePayload, rtExpiresOn) {
191
+ const env = authority.getPreferredCache();
192
+ if (!env) {
193
+ throw createClientAuthError(invalidCacheEnvironment);
194
+ }
195
+ // IdToken: non AAD scenarios can have empty realm
196
+ let cachedIdToken;
197
+ let cachedAccount;
198
+ if (serverTokenResponse.id_token && !!idTokenClaims) {
199
+ cachedIdToken = createIdTokenEntity(this.homeAccountIdentifier, env, serverTokenResponse.id_token, this.clientId, idTokenClaims.tid || "");
200
+ cachedAccount = AccountEntity.createAccount({
201
+ homeAccountId: this.homeAccountIdentifier,
202
+ idTokenClaims: idTokenClaims,
203
+ clientInfo: serverTokenResponse.client_info,
204
+ cloudGraphHostName: authCodePayload?.cloud_graph_host_name,
205
+ msGraphHost: authCodePayload?.msgraph_host,
206
+ }, authority);
207
+ }
208
+ // AccessToken
209
+ let cachedAccessToken = null;
210
+ if (serverTokenResponse.access_token) {
211
+ // If scopes not returned in server response, use request scopes
212
+ const responseScopes = serverTokenResponse.scope
213
+ ? ScopeSet.fromString(serverTokenResponse.scope)
214
+ : new ScopeSet(request.scopes || []);
215
+ /*
216
+ * Use timestamp calculated before request
217
+ * Server may return timestamps as strings, parse to numbers if so.
218
+ */
219
+ const expiresIn = (typeof serverTokenResponse.expires_in === "string"
220
+ ? parseInt(serverTokenResponse.expires_in, 10)
221
+ : serverTokenResponse.expires_in) || 0;
222
+ const extExpiresIn = (typeof serverTokenResponse.ext_expires_in === "string"
223
+ ? parseInt(serverTokenResponse.ext_expires_in, 10)
224
+ : serverTokenResponse.ext_expires_in) || 0;
225
+ const refreshIn = (typeof serverTokenResponse.refresh_in === "string"
226
+ ? parseInt(serverTokenResponse.refresh_in, 10)
227
+ : serverTokenResponse.refresh_in) || undefined;
228
+ const tokenExpirationSeconds = reqTimestamp + expiresIn;
229
+ const extendedTokenExpirationSeconds = tokenExpirationSeconds + extExpiresIn;
230
+ const refreshOnSeconds = refreshIn && refreshIn > 0
231
+ ? reqTimestamp + refreshIn
232
+ : undefined;
233
+ // non AAD scenarios can have empty realm
234
+ cachedAccessToken = createAccessTokenEntity(this.homeAccountIdentifier, env, serverTokenResponse.access_token, this.clientId, idTokenClaims?.tid || authority.tenant, responseScopes.printScopes(), tokenExpirationSeconds, extendedTokenExpirationSeconds, this.cryptoObj.base64Decode, refreshOnSeconds, serverTokenResponse.token_type, userAssertionHash, serverTokenResponse.key_id, request.claims, request.requestedClaimsHash);
235
+ }
236
+ // refreshToken
237
+ let cachedRefreshToken = null;
238
+ if (serverTokenResponse.refresh_token) {
239
+ cachedRefreshToken = createRefreshTokenEntity(this.homeAccountIdentifier, env, serverTokenResponse.refresh_token, this.clientId, serverTokenResponse.foci, userAssertionHash, rtExpiresOn);
240
+ }
241
+ // appMetadata
242
+ let cachedAppMetadata = null;
243
+ if (serverTokenResponse.foci) {
244
+ cachedAppMetadata = AppMetadataEntity.createAppMetadataEntity(this.clientId, env, serverTokenResponse.foci);
245
+ }
246
+ return new CacheRecord(cachedAccount, cachedIdToken, cachedAccessToken, cachedRefreshToken, cachedAppMetadata);
247
+ }
248
+ /**
249
+ * Creates an @AuthenticationResult from @CacheRecord , @IdToken , and a boolean that states whether or not the result is from cache.
250
+ *
251
+ * Optionally takes a state string that is set as-is in the response.
252
+ *
253
+ * @param cacheRecord
254
+ * @param idTokenObj
255
+ * @param fromTokenCache
256
+ * @param stateString
257
+ */
258
+ static async generateAuthenticationResult(cryptoObj, authority, cacheRecord, fromTokenCache, request, idTokenClaims, requestState, serverTokenResponse, requestId) {
259
+ let accessToken = Constants.EMPTY_STRING;
260
+ let responseScopes = [];
261
+ let expiresOn = null;
262
+ let extExpiresOn;
263
+ let refreshOn;
264
+ let familyId = Constants.EMPTY_STRING;
265
+ if (cacheRecord.accessToken) {
266
+ if (cacheRecord.accessToken.tokenType === AuthenticationScheme.POP) {
267
+ const popTokenGenerator = new PopTokenGenerator(cryptoObj);
268
+ const { secret, keyId } = cacheRecord.accessToken;
269
+ if (!keyId) {
270
+ throw createClientAuthError(keyIdMissing);
271
+ }
272
+ accessToken = await popTokenGenerator.signPopToken(secret, keyId, request);
273
+ }
274
+ else {
275
+ accessToken = cacheRecord.accessToken.secret;
276
+ }
277
+ responseScopes = ScopeSet.fromString(cacheRecord.accessToken.target).asArray();
278
+ expiresOn = new Date(Number(cacheRecord.accessToken.expiresOn) * 1000);
279
+ extExpiresOn = new Date(Number(cacheRecord.accessToken.extendedExpiresOn) * 1000);
280
+ if (cacheRecord.accessToken.refreshOn) {
281
+ refreshOn = new Date(Number(cacheRecord.accessToken.refreshOn) * 1000);
282
+ }
283
+ }
284
+ if (cacheRecord.appMetadata) {
285
+ familyId =
286
+ cacheRecord.appMetadata.familyId === THE_FAMILY_ID
287
+ ? THE_FAMILY_ID
288
+ : "";
289
+ }
290
+ const uid = idTokenClaims?.oid || idTokenClaims?.sub || "";
291
+ const tid = idTokenClaims?.tid || "";
292
+ // for hybrid + native bridge enablement, send back the native account Id
293
+ if (serverTokenResponse?.spa_accountid && !!cacheRecord.account) {
294
+ cacheRecord.account.nativeAccountId =
295
+ serverTokenResponse?.spa_accountid;
296
+ }
297
+ const accountInfo = cacheRecord.account
298
+ ? {
299
+ ...cacheRecord.account.getAccountInfo(),
300
+ idTokenClaims,
301
+ }
302
+ : null;
303
+ return {
304
+ authority: authority.canonicalAuthority,
305
+ uniqueId: uid,
306
+ tenantId: tid,
307
+ scopes: responseScopes,
308
+ account: accountInfo,
309
+ idToken: cacheRecord?.idToken?.secret || "",
310
+ idTokenClaims: idTokenClaims || {},
311
+ accessToken: accessToken,
312
+ fromCache: fromTokenCache,
313
+ expiresOn: expiresOn,
314
+ extExpiresOn: extExpiresOn,
315
+ refreshOn: refreshOn,
316
+ correlationId: request.correlationId,
317
+ requestId: requestId || Constants.EMPTY_STRING,
318
+ familyId: familyId,
319
+ tokenType: cacheRecord.accessToken?.tokenType || Constants.EMPTY_STRING,
320
+ state: requestState
321
+ ? requestState.userRequestState
322
+ : Constants.EMPTY_STRING,
323
+ cloudGraphHostName: cacheRecord.account?.cloudGraphHostName ||
324
+ Constants.EMPTY_STRING,
325
+ msGraphHost: cacheRecord.account?.msGraphHost || Constants.EMPTY_STRING,
326
+ code: serverTokenResponse?.spa_code,
327
+ fromNativeBroker: false,
328
+ };
329
+ }
330
330
  }
331
331
 
332
332
  export { ResponseHandler };