diff --git a/frontend/src/components/Modals/Password/index.jsx b/frontend/src/components/Modals/Password/index.jsx index 6be4b3f6aa9c83db332934d8e54f13ea975de754..11da0fc27159e9ca2611b1b8aa5ef91dd76b9ef9 100644 --- a/frontend/src/components/Modals/Password/index.jsx +++ b/frontend/src/components/Modals/Password/index.jsx @@ -17,6 +17,7 @@ export default function PasswordModal({ mode = "single" }) { export function usePasswordModal() { const [auth, setAuth] = useState({ + loading: true, required: false, mode: "single", }); @@ -24,14 +25,26 @@ export function usePasswordModal() { useEffect(() => { async function checkAuthReq() { if (!window) return; - const settings = await System.keys(); + // If the last validity check is still valid + // we can skip the loading. + if (!System.needsAuthCheck()) { + setAuth({ + loading: false, + requiresAuth: false, + mode: "multi", + }); + return; + } + + const settings = await System.keys(); if (settings?.MultiUserMode) { const currentToken = window.localStorage.getItem(AUTH_TOKEN); if (!!currentToken) { const valid = await System.checkAuth(currentToken); if (!valid) { setAuth({ + loading: false, requiresAuth: true, mode: "multi", }); @@ -40,6 +53,7 @@ export function usePasswordModal() { return; } else { setAuth({ + loading: false, requiresAuth: false, mode: "multi", }); @@ -47,6 +61,7 @@ export function usePasswordModal() { } } else { setAuth({ + loading: false, requiresAuth: true, mode: "multi", }); @@ -58,6 +73,7 @@ export function usePasswordModal() { const requiresAuth = settings?.RequiresAuth || false; if (!requiresAuth) { setAuth({ + loading: false, requiresAuth: false, mode: "single", }); @@ -69,6 +85,7 @@ export function usePasswordModal() { const valid = await System.checkAuth(currentToken); if (!valid) { setAuth({ + loading: false, requiresAuth: true, mode: "single", }); @@ -76,6 +93,7 @@ export function usePasswordModal() { return; } else { setAuth({ + loading: false, requiresAuth: false, mode: "single", }); @@ -83,6 +101,7 @@ export function usePasswordModal() { } } else { setAuth({ + loading: false, requiresAuth: true, mode: "single", }); diff --git a/frontend/src/models/system.js b/frontend/src/models/system.js index 219690d185e22447746979d216eaf4af1bade262..6e74eb2a51800b0db027b7ce156b8f0bfbc536d6 100644 --- a/frontend/src/models/system.js +++ b/frontend/src/models/system.js @@ -1,4 +1,4 @@ -import { API_BASE } from "../utils/constants"; +import { API_BASE, AUTH_TIMESTAMP } from "../utils/constants"; import { baseHeaders } from "../utils/request"; const System = { @@ -39,12 +39,22 @@ const System = { .then((res) => res.localFiles) .catch(() => null); }, + needsAuthCheck: function () { + const lastAuthCheck = window.localStorage.getItem(AUTH_TIMESTAMP); + if (!lastAuthCheck) return true; + const expiresAtMs = Number(lastAuthCheck) + 60 * 5 * 1000; // expires in 5 minutes in ms + return Number(new Date()) > expiresAtMs; + }, + checkAuth: async function (currentToken = null) { - return await fetch(`${API_BASE}/system/check-token`, { + const valid = await fetch(`${API_BASE}/system/check-token`, { headers: baseHeaders(currentToken), }) .then((res) => res.ok) .catch(() => false); + + window.localStorage.setItem(AUTH_TIMESTAMP, Number(new Date())); + return valid; }, requestToken: async function (body) { return await fetch(`${API_BASE}/request-token`, { diff --git a/frontend/src/pages/Main/index.jsx b/frontend/src/pages/Main/index.jsx index bfef421f4ca89ee677f1b520703282feddd3320d..0a1e508f6877364a2cfe54f501aa82b0c0318c21 100644 --- a/frontend/src/pages/Main/index.jsx +++ b/frontend/src/pages/Main/index.jsx @@ -5,10 +5,12 @@ import PasswordModal, { usePasswordModal, } from "../../components/Modals/Password"; import { isMobile } from "react-device-detect"; +import { FullScreenLoader } from "../../components/Preloader"; export default function Main() { - const { requiresAuth, mode } = usePasswordModal(); + const { loading, requiresAuth, mode } = usePasswordModal(); + if (loading) return <FullScreenLoader />; if (requiresAuth !== false) { return <>{requiresAuth !== null && <PasswordModal mode={mode} />}</>; } diff --git a/frontend/src/pages/WorkspaceChat/index.jsx b/frontend/src/pages/WorkspaceChat/index.jsx index db065cd2a6ee2fdffccb7385c190811c3f637e58..5b466bda33aa4bfa619625fb7593ab1b0d5889df 100644 --- a/frontend/src/pages/WorkspaceChat/index.jsx +++ b/frontend/src/pages/WorkspaceChat/index.jsx @@ -7,10 +7,12 @@ import PasswordModal, { usePasswordModal, } from "../../components/Modals/Password"; import { isMobile } from "react-device-detect"; +import { FullScreenLoader } from "../../components/Preloader"; export default function WorkspaceChat() { - const { requiresAuth, mode } = usePasswordModal(); + const { loading, requiresAuth, mode } = usePasswordModal(); + if (loading) return <FullScreenLoader />; if (requiresAuth !== false) { return <>{requiresAuth !== null && <PasswordModal mode={mode} />}</>; } diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index fe3a2308e4eb3ba3c443b72de1f2759badf20308..602460fd2878fac0bd308266dca4cdb8fe920e15 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -2,3 +2,4 @@ export const API_BASE = import.meta.env.VITE_API_BASE || "/api"; export const AUTH_USER = "anythingllm_user"; export const AUTH_TOKEN = "anythingllm_authToken"; +export const AUTH_TIMESTAMP = "anythingllm_authTimestamp"; diff --git a/server/endpoints/system.js b/server/endpoints/system.js index 0dceddeab87189508f50eeaa1734732601042606..eed85839ac4fd9194332c12507a398d20664f6e2 100644 --- a/server/endpoints/system.js +++ b/server/endpoints/system.js @@ -316,7 +316,7 @@ function systemEndpoints(app) { updateENV( { - AuthToken: '', + AuthToken: "", JWTSecret: process.env.JWT_SECRET ?? v4(), }, true