fix: migrate token refreshing to use mutex

This commit is contained in:
thecodrr
2021-10-27 11:19:37 +05:00
parent 4e6a6b9246
commit c84b5a28b8

View File

@@ -1,6 +1,7 @@
import http from "../utils/http"; import http from "../utils/http";
import constants from "../utils/constants"; import constants from "../utils/constants";
import { EV, EVENTS } from "../common"; import { EV, EVENTS } from "../common";
import { withTimeout, Mutex } from "async-mutex";
const ENDPOINTS = { const ENDPOINTS = {
token: "/connect/token", token: "/connect/token",
@@ -9,7 +10,6 @@ const ENDPOINTS = {
logout: "/account/logout", logout: "/account/logout",
}; };
var isRefreshingToken = false;
class TokenManager { class TokenManager {
/** /**
* *
@@ -17,13 +17,14 @@ class TokenManager {
*/ */
constructor(storage) { constructor(storage) {
this._storage = storage; this._storage = storage;
this._refreshTokenMutex = withTimeout(new Mutex(), 10 * 1000);
} }
async getToken(renew = true, forceRenew = false) { async getToken(renew = true, forceRenew = false) {
let token = await this._storage.read("token"); let token = await this._storage.read("token");
if (!token) return; if (!token) return;
if (forceRenew || (renew && this._isTokenExpired(token))) { if (forceRenew || (renew && this._isTokenExpired(token))) {
await this._refreshToken(token); await this._refreshToken();
return await this.getToken(); return await this.getToken();
} }
return token; return token;
@@ -49,29 +50,15 @@ class TokenManager {
} }
} }
async _refreshToken(token) { async _refreshToken() {
const { refresh_token, scope } = token; await this._refreshTokenMutex.runExclusive(async () => {
if (!refresh_token || !scope) return; const token = await this.getToken(false, false);
if (!this._isTokenExpired(token)) {
return;
}
if (isRefreshingToken) { const { refresh_token, scope } = token;
return new Promise((resolve, reject) => { if (!refresh_token || !scope) return;
EV.subscribe(EVENTS.tokenRefreshed, onTokenRefreshed, true);
function onTokenRefreshed({ success, error }) {
clearTimeout(timeout);
if (success) return resolve();
else return reject(error);
}
const timeout = setTimeout(() => {
EV.unsubscribe(EVENTS.tokenRefreshed, onTokenRefreshed);
reject("Timeout while refreshing token.");
}, 15000);
});
}
isRefreshingToken = true;
try {
await this.saveToken( await this.saveToken(
await http.post(`${constants.AUTH_HOST}${ENDPOINTS.token}`, { await http.post(`${constants.AUTH_HOST}${ENDPOINTS.token}`, {
refresh_token, refresh_token,
@@ -80,17 +67,8 @@ class TokenManager {
client_id: "notesnook", client_id: "notesnook",
}) })
); );
EV.publish(EVENTS.tokenRefreshed, { success: true }); EV.publish(EVENTS.tokenRefreshed);
} catch (e) { });
EV.publish(EVENTS.tokenRefreshed, {
success: false,
error: e,
});
console.error("Failed to refresh token:", e);
throw e;
} finally {
isRefreshingToken = false;
}
} }
async revokeToken() { async revokeToken() {