diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 885f3d7..2cea909 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -8,6 +8,8 @@
"name": "frontend",
"version": "0.0.0",
"dependencies": {
+ "@react-keycloak/web": "^3.4.0",
+ "keycloak-js": "^26.2.0",
"react": "^19.1.0",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^19.1.0",
@@ -962,6 +964,46 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@react-keycloak/core": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@react-keycloak/core/-/core-3.2.0.tgz",
+ "integrity": "sha512-1yzU7gQzs+6E1v6hGqxy0Q+kpMHg9sEcke2yxZR29WoU8KNE8E50xS6UbI8N7rWsgyYw8r9W1cUPCOF48MYjzw==",
+ "dependencies": {
+ "react-fast-compare": "^3.2.0"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://www.patreon.com/reactkeycloak"
+ },
+ "peerDependencies": {
+ "react": ">=16"
+ }
+ },
+ "node_modules/@react-keycloak/web": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/@react-keycloak/web/-/web-3.4.0.tgz",
+ "integrity": "sha512-yKKSCyqBtn7dt+VckYOW1IM5NW999pPkxDZOXqJ6dfXPXstYhOQCkTZqh8l7UL14PkpsoaHDh7hSJH8whah01g==",
+ "dependencies": {
+ "@babel/runtime": "^7.9.0",
+ "@react-keycloak/core": "^3.2.0",
+ "hoist-non-react-statics": "^3.3.2"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://www.patreon.com/reactkeycloak"
+ },
+ "peerDependencies": {
+ "keycloak-js": ">=9.0.2",
+ "react": ">=16.8",
+ "react-dom": ">=16.8",
+ "typescript": ">=3.8"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.9",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.9.tgz",
@@ -2080,6 +2122,11 @@
"node": ">=6"
}
},
+ "node_modules/keycloak-js": {
+ "version": "26.2.0",
+ "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-26.2.0.tgz",
+ "integrity": "sha512-CrFcXTN+d6J0V/1v3Zpioys6qHNWE6yUzVVIsCUAmFn9H14GZ0vuYod+lt+SSpMgWGPuneDZBSGBAeLBFuqjsw=="
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -2403,6 +2450,11 @@
"react": "^19.1.0"
}
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
+ },
"node_modules/react-icons": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 45ce11c..22f00e0 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -10,6 +10,8 @@
"preview": "vite preview"
},
"dependencies": {
+ "@react-keycloak/web": "^3.4.0",
+ "keycloak-js": "^26.2.0",
"react": "^19.1.0",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^19.1.0",
diff --git a/frontend/src/keycloak.js b/frontend/src/keycloak.js
new file mode 100644
index 0000000..1971f37
--- /dev/null
+++ b/frontend/src/keycloak.js
@@ -0,0 +1,9 @@
+import Keycloak from 'keycloak-js';
+
+const keycloak = new Keycloak({
+ url: 'https://keycloak.dvirlabs.com/',
+ realm: 'lab', // ⬅️ Change to your realm name
+ clientId: 'navix', // ⬅️ Change to your client ID in Keycloak
+});
+
+export default keycloak;
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx
index b9a1a6d..c255fb6 100644
--- a/frontend/src/main.jsx
+++ b/frontend/src/main.jsx
@@ -1,10 +1,32 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import './index.css'
-import App from './App.jsx'
+import { StrictMode, useState, useEffect } from 'react';
+import { createRoot } from 'react-dom/client';
+import './index.css';
+import App from './App.jsx';
+import keycloak from './keycloak';
+
+function SecuredApp() {
+ const [authenticated, setAuthenticated] = useState(false);
+
+ useEffect(() => {
+ keycloak.init({ onLoad: 'login-required' }).then((auth) => {
+ if (!auth) {
+ window.location.reload();
+ } else {
+ setAuthenticated(true);
+ }
+ });
+
+ // Optional: refresh token
+ setInterval(() => {
+ keycloak.updateToken(70).catch(() => keycloak.logout());
+ }, 6000);
+ }, []);
+
+ return authenticated ?