1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Fix token refresh racing with other requests and not using new token (#4798)

* Fix token refresh racing with other requests and not using new token

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2025-04-14 10:11:55 +01:00
committed by GitHub
parent 1ba4412260
commit 480c8e86a4
2 changed files with 88 additions and 14 deletions

View File

@@ -158,25 +158,28 @@ export class FetchHttpApi<O extends IHttpOpts> {
// avoid mutating paramOpts so they can be used on retry
const opts = { ...paramOpts };
if (this.opts.accessToken) {
// Await any ongoing token refresh before we build the headers/params
await this.tokenRefreshPromise;
// Take a copy of the access token so we have a record of the token we used for this request if it fails
const accessToken = this.opts.accessToken;
if (accessToken) {
if (this.opts.useAuthorizationHeader) {
if (!opts.headers) {
opts.headers = {};
}
if (!opts.headers.Authorization) {
opts.headers.Authorization = "Bearer " + this.opts.accessToken;
opts.headers.Authorization = `Bearer ${accessToken}`;
}
if (queryParams.access_token) {
delete queryParams.access_token;
}
} else if (!queryParams.access_token) {
queryParams.access_token = this.opts.accessToken;
queryParams.access_token = accessToken;
}
}
try {
// Await any ongoing token refresh
await this.tokenRefreshPromise;
const response = await this.request<T>(method, path, queryParams, body, opts);
return response;
} catch (error) {
@@ -185,15 +188,21 @@ export class FetchHttpApi<O extends IHttpOpts> {
}
if (error.errcode === "M_UNKNOWN_TOKEN" && !opts.doNotAttemptTokenRefresh) {
const tokenRefreshPromise = this.tryRefreshToken();
this.tokenRefreshPromise = Promise.allSettled([tokenRefreshPromise]);
const outcome = await tokenRefreshPromise;
// If the access token has changed since we started the request, but before we refreshed it,
// then it was refreshed due to another request failing, so retry before refreshing again.
let outcome: TokenRefreshOutcome | null = null;
if (accessToken === this.opts.accessToken) {
const tokenRefreshPromise = this.tryRefreshToken();
this.tokenRefreshPromise = tokenRefreshPromise;
outcome = await tokenRefreshPromise;
}
if (outcome === TokenRefreshOutcome.Success) {
if (outcome === TokenRefreshOutcome.Success || outcome === null) {
// if we got a new token retry the request
return this.authedRequest(method, path, queryParams, body, {
...paramOpts,
doNotAttemptTokenRefresh: true,
// Only attempt token refresh once for each failed request
doNotAttemptTokenRefresh: outcome !== null,
});
}
if (outcome === TokenRefreshOutcome.Failure) {