From 2d9ff8831a3d222db10628e9c641766d0681ea79 Mon Sep 17 00:00:00 2001 From: RekHoto Date: Fri, 8 Dec 2023 11:38:25 +0400 Subject: [PATCH] final kk --- package.json | 2 ++ src/App.jsx | 25 +++++++++-------- src/SignOut.jsx | 11 ++------ src/WithAuth.jsx | 20 ++++++-------- src/api.js | 12 +++++---- src/keycloak.js | 7 +++++ src/pages/Login.jsx | 18 ++----------- src/stores/auth.js | 66 --------------------------------------------- 8 files changed, 42 insertions(+), 119 deletions(-) create mode 100644 src/keycloak.js diff --git a/package.json b/package.json index 97481eb..9fa86b3 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@ant-design/icons": "^5.0.1", "@nanostores/react": "^0.4.1", + "@react-keycloak/web": "^3.4.0", "@tanstack/react-query": "^4.24.9", "@turf/bbox": "^6.5.0", "@turf/helpers": "^6.5.0", @@ -20,6 +21,7 @@ "chart.js": "^4.4.0", "immer": "^9.0.19", "immutable": "^4.3.0", + "keycloak-js": "^23.0.1", "lodash.debounce": "^4.0.8", "mapbox-gl": "npm:empty-npm-package@1.0.0", "maplibre-gl": "^2.4.0", diff --git a/src/App.jsx b/src/App.jsx index bee1555..ac714f8 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -11,7 +11,8 @@ import { usePendingPointsFilters } from "./stores/usePendingPointsFilters"; import { useOnApprovalPointsFilters } from "./stores/useOnApprovalPointsFilters"; import { useWorkingPointsFilters } from "./stores/useWorkingPointsFilters"; import useLocalStorage from "./hooks/useLocalStorage.js"; - +import { ReactKeycloakProvider } from "@react-keycloak/web"; +import { keycloak } from "./keycloak.js"; const queryClient = new QueryClient(); enableMapSet(); @@ -35,16 +36,18 @@ function App() { } return ( - - - - } /> - } /> - } /> - {/*} />*/} - - - + + + + + } /> + } /> + } /> + {/*} />*/} + + + + ); } diff --git a/src/SignOut.jsx b/src/SignOut.jsx index 2b8351c..c491d9f 100644 --- a/src/SignOut.jsx +++ b/src/SignOut.jsx @@ -1,16 +1,9 @@ import { Button, Popover, Tooltip } from "antd"; import { ArrowRightOutlined, LogoutOutlined } from "@ant-design/icons"; import { api } from "./api"; -import { setAuth } from "./stores/auth"; import { useQuery } from "@tanstack/react-query"; import { Title } from "./components/Title"; - -export const logOut = () => { - localStorage.removeItem('access_token'); - localStorage.removeItem('refresh_token'); - localStorage.removeItem('expires_in'); - setAuth(false); -}; +import { keycloak } from "./keycloak.js"; export function SignOut() { const { data } = useQuery(["profile"], async () => { @@ -24,7 +17,7 @@ export function SignOut() { content={ <> - <Button type="primary" block onClick={logOut}> + <Button type="primary" block onClick={keycloak.logout}> <span className="mr-1">Выйти</span> <ArrowRightOutlined /> </Button> diff --git a/src/WithAuth.jsx b/src/WithAuth.jsx index 5ac29a4..cce46c0 100644 --- a/src/WithAuth.jsx +++ b/src/WithAuth.jsx @@ -1,21 +1,17 @@ -import { useStore } from "@nanostores/react"; -import { Spin } from "antd"; - -import { Navigate } from "react-router-dom"; - -import { isAuthorized$, userInfoLoading$ } from "./stores/auth"; +import { useKeycloak } from "@react-keycloak/web"; export function WithAuth(props) { - const isAuthorized = useStore(isAuthorized$); - const userInfoLoading = useStore(userInfoLoading$); + const { keycloak } = useKeycloak(); - if (userInfoLoading) { - return <Spin className="user-info-loader" />; + const login = async () => { + await keycloak.login(); } - if (isAuthorized) { + if (keycloak.authenticated) { return <>{props.children}</>; + } else { + login() } - return <Navigate to="/signin" replace={true} />; + return <></>; } diff --git a/src/api.js b/src/api.js index 4816817..a8a74d1 100644 --- a/src/api.js +++ b/src/api.js @@ -7,6 +7,8 @@ import { appendFiltersInUse } from "./utils.js"; import { useMode } from "./stores/useMode.js"; import { useMemo } from "react"; import { useUpdateLayerCounter } from "./stores/useUpdateLayerCounter.js"; +import { keycloak } from "./keycloak.js"; +import { useKeycloak } from "@react-keycloak/web"; export const BASE_URL = import.meta.env.VITE_API_URL; @@ -17,10 +19,10 @@ export const api = axios.create({ : BASE_URL, }); -api.interceptors.request.use(function (config) { - const token = localStorage.getItem("access_token"); +api.interceptors.request.use(async function (config) { + const token = keycloak.token; if (token) { - config.headers.Authorization = `Bearer ${token}`; + config.headers.Authorization = `Bearer ${ token }`; } return config; @@ -235,14 +237,14 @@ const TASK_STATUSES = { finished: "Перерасчет ML завершен" } export const useCanEdit = () => { - const { data } = useGetPermissions(); + const { keycloak } = useKeycloak(); const { data: statusData } = useLastMLRun(); const hasFinishedUpdate = useMemo(() => { return statusData?.task_status === TASK_STATUSES.finished }, [statusData]); - return data === "editor" && hasFinishedUpdate; + return keycloak.hasResourceRole("postnet_editor", "postnet") && hasFinishedUpdate; }; export const useUpdatePostamatId = () => { diff --git a/src/keycloak.js b/src/keycloak.js new file mode 100644 index 0000000..de12957 --- /dev/null +++ b/src/keycloak.js @@ -0,0 +1,7 @@ +import Keycloak from "keycloak-js"; + +export const keycloak = new Keycloak({ + url: import.meta.env.VITE_KEYCLOAK_URL, + realm: "SST", + clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID, +}); diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 633142c..6ea2f5f 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -3,27 +3,13 @@ import { Alert, Button, Form, Input, Space, Typography } from "antd"; import { LockOutlined, UserOutlined } from "@ant-design/icons"; import React from "react"; import { Navigate } from "react-router-dom"; -import { isAuthorized$, refreshTokenIntervalFunction, setAuthLocalStorage } from "../stores/auth"; -import { signin, signinError$, signinLoading$ } from "../stores/signin"; +import { isAuthorized$ } from "../stores/auth"; +import { signinError$, signinLoading$ } from "../stores/signin"; function LoginForm() { const signinError = useStore(signinError$); const signinLoading = useStore(signinLoading$); - const onFinish = async (values) => { - const data = await signin(values); - setAuthLocalStorage(data); - - let interval; - - setTimeout(async () => { - await refreshTokenIntervalFunction(); - interval = setInterval(async () => { - await refreshTokenIntervalFunction(); - }, (data.expires_in - 5) * 1000) - }, 0); - }; - return ( <Space direction="vertical" style={{ width: "320px" }}> {signinError.length > 0 ? ( diff --git a/src/stores/auth.js b/src/stores/auth.js index ac52517..971ad69 100644 --- a/src/stores/auth.js +++ b/src/stores/auth.js @@ -1,6 +1,4 @@ import { action, atom } from "nanostores"; -import { api } from "../api"; -import { logOut } from "../SignOut.jsx"; export const userInfoLoading$ = atom(true); @@ -9,67 +7,3 @@ export const isAuthorized$ = atom(false); export const setAuth = action(isAuthorized$, "setAuth", (store, newValue) => { store.set(newValue); }); - -export const refreshToken = () => { - const url = import.meta.env.VITE_KEYCLOAK_URL || 'https://kk.dev.selftech.ru/'; - const clientId = import.meta.env.VITE_KEYCLOAK_CLIENT_ID || 'postnet'; - const clientSecret = import.meta.env.VITE_KEYCLOAK_CLIENT_SECRET || 'K2yHweEUispkVeWn03VMk843sW2Moic5'; - - return api.request({ - url: "/realms/SST/protocol/openid-connect/token", - baseURL: url, - method: "POST", - data: { - grant_type: "refresh_token", - client_id: clientId, - client_secret: clientSecret, - token_type: "bearer", - refresh_token: localStorage.getItem("refresh_token") - }, - headers: { - 'Content-type': 'application/x-www-form-urlencoded', - }, - }); -} - -export const setAuthLocalStorage = (data) => { - localStorage.setItem("access_token", data.access_token); - localStorage.setItem("refresh_token", data.refresh_token); - localStorage.setItem("expires_in", data.expires_in); -} - -export const refreshTokenIntervalFunction = async () => { - try { - const { data: refreshData } = await refreshToken(); - setAuthLocalStorage(refreshData); - } catch (error) { - clearInterval(interval); - logOut(); - throw error; - } -} - -async function checkAuth() { - try { - const { data } = await refreshToken(); - setAuthLocalStorage(data); - - let interval; - - setTimeout(async () => { - await refreshTokenIntervalFunction(); - interval = setInterval(async () => { - await refreshTokenIntervalFunction(); - }, (data.expires_in - 5) * 1000) - }, 0); - - setAuth(true); - } catch (e) { - console.log("Not authorized"); - clearInterval(interval); - } finally { - userInfoLoading$.set(false); - } -} - -checkAuth();