2020-12-16 12:06:25 +05:00
|
|
|
import http from "../utils/http";
|
|
|
|
|
import constants from "../utils/constants";
|
2021-08-08 12:20:07 +05:00
|
|
|
import { EV, EVENTS } from "../common";
|
2021-10-27 11:19:37 +05:00
|
|
|
import { withTimeout, Mutex } from "async-mutex";
|
2020-12-16 12:06:25 +05:00
|
|
|
|
|
|
|
|
const ENDPOINTS = {
|
|
|
|
|
token: "/connect/token",
|
|
|
|
|
revoke: "/connect/revocation",
|
2020-12-22 09:47:01 +05:00
|
|
|
temporaryToken: "/account/token",
|
2021-01-04 11:22:24 +05:00
|
|
|
logout: "/account/logout",
|
2020-12-16 12:06:25 +05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TokenManager {
|
|
|
|
|
/**
|
|
|
|
|
*
|
2021-09-26 11:47:13 +05:00
|
|
|
* @param {import("../database/storage").default} storage
|
2020-12-16 12:06:25 +05:00
|
|
|
*/
|
2021-09-26 11:47:13 +05:00
|
|
|
constructor(storage) {
|
|
|
|
|
this._storage = storage;
|
2021-10-27 11:19:37 +05:00
|
|
|
this._refreshTokenMutex = withTimeout(new Mutex(), 10 * 1000);
|
2020-12-16 12:06:25 +05:00
|
|
|
}
|
|
|
|
|
|
2021-05-25 16:19:58 +05:00
|
|
|
async getToken(renew = true, forceRenew = false) {
|
2021-09-26 11:47:13 +05:00
|
|
|
let token = await this._storage.read("token");
|
2020-12-16 12:06:25 +05:00
|
|
|
if (!token) return;
|
2021-05-25 16:19:58 +05:00
|
|
|
if (forceRenew || (renew && this._isTokenExpired(token))) {
|
2021-11-01 11:53:28 +05:00
|
|
|
await this._refreshToken(forceRenew);
|
2020-12-16 12:06:25 +05:00
|
|
|
return await this.getToken();
|
|
|
|
|
}
|
|
|
|
|
return token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_isTokenExpired(token) {
|
|
|
|
|
const { t, expires_in } = token;
|
2021-08-08 12:20:07 +05:00
|
|
|
const expiryMs = t + expires_in * 1000;
|
2020-12-16 12:06:25 +05:00
|
|
|
return Date.now() >= expiryMs;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-25 16:19:58 +05:00
|
|
|
async getAccessToken(forceRenew = false) {
|
|
|
|
|
try {
|
|
|
|
|
const token = await this.getToken(true, forceRenew);
|
|
|
|
|
if (!token) return;
|
|
|
|
|
return token.access_token;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Error getting access token:", e);
|
|
|
|
|
if (e.message === "invalid_grant" || e.message === "invalid_client") {
|
2021-12-04 10:19:33 +05:00
|
|
|
EV.publish(EVENTS.userSessionExpired);
|
2021-05-25 16:19:58 +05:00
|
|
|
}
|
2021-09-13 09:37:52 +05:00
|
|
|
throw e;
|
2021-05-25 16:19:58 +05:00
|
|
|
}
|
2020-12-16 12:06:25 +05:00
|
|
|
}
|
|
|
|
|
|
2021-11-01 11:53:28 +05:00
|
|
|
async _refreshToken(forceRenew = false) {
|
2021-10-27 11:19:37 +05:00
|
|
|
await this._refreshTokenMutex.runExclusive(async () => {
|
|
|
|
|
const token = await this.getToken(false, false);
|
2021-11-01 11:53:28 +05:00
|
|
|
if (!forceRenew && !this._isTokenExpired(token)) {
|
2021-10-27 11:19:37 +05:00
|
|
|
return;
|
|
|
|
|
}
|
2021-08-08 12:20:07 +05:00
|
|
|
|
2021-10-27 11:19:37 +05:00
|
|
|
const { refresh_token, scope } = token;
|
|
|
|
|
if (!refresh_token || !scope) return;
|
2021-08-08 12:20:07 +05:00
|
|
|
await this.saveToken(
|
|
|
|
|
await http.post(`${constants.AUTH_HOST}${ENDPOINTS.token}`, {
|
|
|
|
|
refresh_token,
|
|
|
|
|
grant_type: "refresh_token",
|
|
|
|
|
scope: scope,
|
|
|
|
|
client_id: "notesnook",
|
|
|
|
|
})
|
|
|
|
|
);
|
2021-10-27 11:19:37 +05:00
|
|
|
EV.publish(EVENTS.tokenRefreshed);
|
|
|
|
|
});
|
2020-12-16 12:06:25 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async revokeToken() {
|
|
|
|
|
const token = await this.getToken();
|
|
|
|
|
if (!token) return;
|
2021-01-04 11:22:24 +05:00
|
|
|
const { access_token } = token;
|
2020-12-16 12:06:25 +05:00
|
|
|
|
2021-01-04 11:22:24 +05:00
|
|
|
await http.post(
|
|
|
|
|
`${constants.AUTH_HOST}${ENDPOINTS.logout}`,
|
|
|
|
|
null,
|
|
|
|
|
access_token
|
|
|
|
|
);
|
2020-12-16 12:06:25 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
saveToken(tokenResponse) {
|
2021-05-28 23:10:10 +05:00
|
|
|
let token = { ...tokenResponse, t: Date.now() };
|
2021-09-26 11:47:13 +05:00
|
|
|
return this._storage.write("token", token);
|
2021-04-05 11:32:40 +05:00
|
|
|
}
|
|
|
|
|
|
2020-12-22 09:47:01 +05:00
|
|
|
async getAccessTokenFromAuthorizationCode(userId, authCode) {
|
|
|
|
|
return await this.saveToken(
|
|
|
|
|
await http.post(`${constants.AUTH_HOST}${ENDPOINTS.temporaryToken}`, {
|
|
|
|
|
authorization_code: authCode,
|
|
|
|
|
user_id: userId,
|
|
|
|
|
client_id: "notesnook",
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
2020-12-16 12:06:25 +05:00
|
|
|
}
|
|
|
|
|
export default TokenManager;
|