Skip to content

Commit 95ea73a

Browse files
committed
frontend/utils: use navigator.locks to synchronize tabs.
With this synchronization only one jwt_refresh can be in flight at once per browser. This definitely fixes the issue with users getting logged out due to concurrent jwt_refreshes.
1 parent f1d1ed4 commit 95ea73a

File tree

1 file changed

+27
-41
lines changed

1 file changed

+27
-41
lines changed

frontend/src/utils.tsx

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -89,52 +89,38 @@ const AuthMiddleware: Middleware = {
8989
}
9090
fetchClient.use(AuthMiddleware);
9191

92-
// This promise is used to synchronize the timeout and Frame component refreshing the access token
93-
let refreshPromise: Promise<void>;
94-
let refreshPromiseResolved = true;
95-
96-
// This function must be called only at one place. Use the promise instead if you need to ensure that you have a valid token.
97-
export function refresh_access_token() {
98-
if (refreshPromiseResolved) {
99-
refreshPromiseResolved = false;
100-
} else {
101-
return refreshPromise;
92+
export async function refresh_access_token() {
93+
if (window.location.pathname == "/recovery") {
94+
loggedIn.value = AppState.Recovery;
95+
return;
10296
}
103-
refreshPromise = new Promise(async (resolve, reject) => {
104-
if (window.location.pathname == "/recovery") {
105-
loggedIn.value = AppState.Recovery;
106-
resolve();
107-
return;
108-
}
10997

110-
try {
111-
const {error, response} = await fetchClient.GET("/auth/jwt_refresh", {credentials: "same-origin"});
112-
113-
if (!error || response.status === 502) {
114-
if (!localStorage.getItem("loginSalt") || !localStorage.getItem("secretKey")) {
115-
logout(false);
116-
}
117-
loggedIn.value = AppState.LoggedIn;
118-
} else {
119-
auth_already_failed = true;
120-
localStorage.removeItem("loginSalt");
121-
localStorage.removeItem("secretKey");
122-
loggedIn.value = AppState.LoggedOut;
123-
}
124-
refreshPromiseResolved = true;
125-
resolve();
126-
} catch (e) {
98+
try {
99+
const {error, response} = await window.navigator.locks.request("refreshLock", async () => {
100+
101+
const resp = await fetchClient.GET("/auth/jwt_refresh", {credentials: "same-origin"});
102+
return resp;
103+
});
127104

128-
//This means we are logged in but the refresh failed
129-
if (localStorage.getItem("loginSalt") && localStorage.getItem("secretKey")) {
130-
loggedIn.value = AppState.LoggedIn;
105+
if (!error || response.status === 502) {
106+
if (!localStorage.getItem("loginSalt") || !localStorage.getItem("secretKey")) {
107+
logout(false);
131108
}
132-
console.error(e);
133-
refreshPromiseResolved = true;
134-
resolve();
109+
loggedIn.value = AppState.LoggedIn;
110+
} else {
111+
auth_already_failed = true;
112+
localStorage.removeItem("loginSalt");
113+
localStorage.removeItem("secretKey");
114+
loggedIn.value = AppState.LoggedOut;
135115
}
136-
});
137-
return refreshPromise
116+
} catch (e) {
117+
118+
//This means we are logged in but the refresh failed
119+
if (localStorage.getItem("loginSalt") && localStorage.getItem("secretKey")) {
120+
loggedIn.value = AppState.LoggedIn;
121+
}
122+
console.error(e);
123+
}
138124
}
139125

140126
export let secret: Uint8Array;

0 commit comments

Comments
 (0)