From 325128254adf25a5ff5c319d124e50d6ef5528f5 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Thu, 3 Jul 2025 20:56:29 +0300 Subject: [PATCH 1/2] feat: add token auto-refresh configuration in application.properties --- src/main/resources/application.properties | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 974e6638..49ad5d6e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -75,3 +75,7 @@ quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG quarkus.rest-client.logging.scope=request-response quarkus.rest-client.logging.body-limit=10000 quarkus.oidc.authentication.add-openid-scope=true + +# Token refresh configuration - auto refresh tokens on activity +quarkus.oidc.token.refresh-expired=true +quarkus.oidc.token.refresh-token-time-skew=10M From c4189c3bd30e2152118727f5fb2955e7263ecad9 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Thu, 10 Jul 2025 13:07:45 +0300 Subject: [PATCH 2/2] fix: try to fix token expiration (without result) --- src/main/resources/application.properties | 8 +++-- src/main/webui/package-lock.json | 34 +++++++++++++++++++ src/main/webui/package.json | 3 +- src/main/webui/src/App.tsx | 22 +++++++----- .../webui/src/components/ClustersTable.tsx | 25 +++++--------- src/main/webui/src/components/EnvTable.tsx | 24 +++++-------- 6 files changed, 73 insertions(+), 43 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 474f6b21..82f46e88 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -80,6 +80,10 @@ quarkus.rest-client.logging.scope=request-response quarkus.rest-client.logging.body-limit=10000 quarkus.oidc.authentication.add-openid-scope=true -# Token refresh configuration - auto refresh tokens on activity +# Session and token management +quarkus.oidc.authentication.session-age-extension=30M +quarkus.oidc.authentication.restore-path-after-redirect=true +quarkus.oidc.authentication.cookie-path=/ +quarkus.oidc.authentication.cookie-same-site=lax quarkus.oidc.token.refresh-expired=true -quarkus.oidc.token.refresh-token-time-skew=10M +quarkus.oidc.token.auto-refresh-interval=2M diff --git a/src/main/webui/package-lock.json b/src/main/webui/package-lock.json index e95d2a62..4581476a 100644 --- a/src/main/webui/package-lock.json +++ b/src/main/webui/package-lock.json @@ -22,6 +22,7 @@ "@types/node": "^16.18.126", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", + "axios": "^1.7.0", "dayjs": "^1.11.13", "react": "^19.1.0", "react-dom": "^19.1.0", @@ -5508,6 +5509,33 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -14292,6 +14320,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", diff --git a/src/main/webui/package.json b/src/main/webui/package.json index 8ff5bae3..3133d66f 100644 --- a/src/main/webui/package.json +++ b/src/main/webui/package.json @@ -22,7 +22,8 @@ "react-dom": "^19.1.0", "react-scripts": "5.0.1", "typescript": "^4.9.5", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "axios": "^1.7.0" }, "scripts": { "start": "react-scripts start", diff --git a/src/main/webui/src/App.tsx b/src/main/webui/src/App.tsx index 3952ece1..587a0489 100644 --- a/src/main/webui/src/App.tsx +++ b/src/main/webui/src/App.tsx @@ -6,6 +6,9 @@ import LogoutButton from "./components/LogoutButton"; import {UserInfo} from "./entities/users"; import TabPanel from "./components/TabPanel"; import {AppMetadata} from "./entities/metadata"; +import {httpClient} from "./utils/httpClient"; +import {useSessionKeepAlive} from "./hooks/useSessionKeepAlive"; +import {AxiosResponse} from "axios"; function App() { @@ -13,21 +16,22 @@ function App() { const [userInfo, setUserInfo] = useState({authenticated: false}); const [metadata, setMetadata] = useState(null); const [metadataLoading, setMetadataLoading] = useState(true); + + // Keep session alive during user activity + useSessionKeepAlive(); useEffect(() => { - const fetchAuthStatus = fetch("/colly/auth-status") - .then(res => res.json()) - .then(authData => { - setUserInfo(authData); + const fetchAuthStatus = httpClient.get("/colly/auth-status") + .then((response: AxiosResponse) => { + setUserInfo(response.data); }) - .catch(err => { + .catch((err: any) => { console.error("Failed to fetch auth status:", err); }); - const fetchMetadata = fetch("/colly/metadata") - .then(res => res.json()) - .then((metaData: AppMetadata) => setMetadata(metaData)) - .catch(err => { + const fetchMetadata = httpClient.get("/colly/metadata") + .then((response: AxiosResponse) => setMetadata(response.data)) + .catch((err: any) => { console.error("Failed to fetch app metadata:", err); setMetadata({ monitoringColumns: [] diff --git a/src/main/webui/src/components/ClustersTable.tsx b/src/main/webui/src/components/ClustersTable.tsx index 898e6078..e63ae572 100644 --- a/src/main/webui/src/components/ClustersTable.tsx +++ b/src/main/webui/src/components/ClustersTable.tsx @@ -15,6 +15,8 @@ import EditIcon from "@mui/icons-material/Edit"; import EditClusterDialog from "./EditClusterDialog"; import SearchIcon from "@mui/icons-material/Search"; import CancelIcon from "@mui/icons-material/Cancel"; +import {httpClient} from "../utils/httpClient"; +import {AxiosResponse} from "axios"; interface ClusterTableProps { userInfo: UserInfo; @@ -28,10 +30,9 @@ export default function ClustersTable({userInfo}: ClusterTableProps) { useEffect(() => { - fetch("/colly/clusters") - .then(res => res.json()) - .then(clustersData => setClusters(clustersData)) - .catch(err => console.error("Failed to fetch clusters:", err)) + httpClient.get("/colly/clusters") + .then((response: AxiosResponse) => setClusters(response.data)) + .catch((err: any) => console.error("Failed to fetch clusters:", err)) .finally(() => setLoading(false)); }, []); @@ -50,18 +51,10 @@ export default function ClustersTable({userInfo}: ClusterTableProps) { } formData.append("name", changedCluster.name); - const response = await fetch(`/colly/clusters/${changedCluster.name}`, { - method: "POST", - body: formData - }); - - if (response.ok) { - setSelectedCluster(null); - setClusters(prev => prev.map(cluster => cluster.name === changedCluster.name ? changedCluster : cluster)); - } else { - console.error("Failed to save changes", await response.text()); - } - } catch (error) { + await httpClient.post(`/colly/clusters/${changedCluster.name}`, formData); + setSelectedCluster(null); + setClusters(prev => prev.map(cluster => cluster.name === changedCluster.name ? changedCluster : cluster)); + } catch (error: any) { console.error("Error during save:", error); } }; diff --git a/src/main/webui/src/components/EnvTable.tsx b/src/main/webui/src/components/EnvTable.tsx index 497fb1c8..5e567d10 100644 --- a/src/main/webui/src/components/EnvTable.tsx +++ b/src/main/webui/src/components/EnvTable.tsx @@ -21,6 +21,8 @@ import EditEnvironmentDialog from "./EditEnvironmentDialog"; import {Environment, ENVIRONMENT_TYPES_MAPPING, EnvironmentStatus, STATUS_MAPPING} from "../entities/environments"; import {UserInfo} from "../entities/users"; import dayjs from "dayjs"; +import {httpClient} from "../utils/httpClient"; +import {AxiosResponse} from "axios"; interface EnvTableProps { userInfo: UserInfo; @@ -39,9 +41,9 @@ export default function EnvTable({userInfo, monitoringColumns}: EnvTableProps) { const [isInitialized, setIsInitialized] = useState(false); useEffect(() => { - fetch("/colly/environments").then(res => res.json()) - .then(envData => setEnvironments(envData)) - .catch(err => console.error("Failed to fetch environments:", err)) + httpClient.get("/colly/environments") + .then((response: AxiosResponse) => setEnvironments(response.data)) + .catch((err: any) => console.error("Failed to fetch environments:", err)) .finally(() => setLoading(false)); }, []); @@ -125,18 +127,10 @@ export default function EnvTable({userInfo, monitoringColumns}: EnvTableProps) { formData.append("expirationDate", changedEnv.expirationDate ? dayjs(changedEnv.expirationDate).format("YYYY-MM-DD") : ""); changedEnv.labels.forEach(label => formData.append("labels", label)); - const response = await fetch(`/colly/environments/${changedEnv.id}`, { - method: "POST", - body: formData - }); - - if (response.ok) { - setSelectedEnv(null); - setEnvironments(prev => prev.map(env => env.id === changedEnv.id ? changedEnv : env)); - } else { - console.error("Failed to save changes", await response.text()); - } - } catch (error) { + await httpClient.post(`/colly/environments/${changedEnv.id}`, formData); + setSelectedEnv(null); + setEnvironments(prev => prev.map(env => env.id === changedEnv.id ? changedEnv : env)); + } catch (error: any) { console.error("Error during save:", error); } }, []);