fix on logout auth.error - change oidp provider to keycloak-js
This commit is contained in:
parent
e074e0ef4e
commit
6de8e3ecd6
@ -1,3 +1,4 @@
|
||||
FRIGATE_PROXY=http://localhost:4000
|
||||
OPENID_SERVER=https://your.server.com:443/realms/your-realm
|
||||
REALM=frigate-realm
|
||||
CLIENT_ID=frontend-client
|
||||
@ -8,7 +8,8 @@ services:
|
||||
- /etc/localtime:/etc/localtime:ro # for Unix Time
|
||||
environment:
|
||||
FRIGATE_PROXY: http://localhost:4000
|
||||
OPENID_SERVER: https://server:port/realms/your-realm
|
||||
OPENID_SERVER: https://server:port
|
||||
CLIENT_ID: frontend-client
|
||||
REALM: frigate-realm
|
||||
ports:
|
||||
- 80:80 # set your port here
|
||||
@ -12,6 +12,7 @@
|
||||
"@mantine/modals": "^6.0.16",
|
||||
"@mantine/notifications": "^6.0.16",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@react-keycloak/web": "^3.4.0",
|
||||
"@tabler/icons-react": "^2.24.0",
|
||||
"@tanstack/react-query": "^5.21.2",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
@ -35,18 +36,17 @@
|
||||
"i18next-browser-languagedetector": "^7.2.0",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"keycloak-js": "^24.0.1",
|
||||
"mantine-react-table": "^1.0.0-beta.25",
|
||||
"mobx": "^6.9.0",
|
||||
"mobx-react-lite": "^3.4.3",
|
||||
"mobx-utils": "^6.0.7",
|
||||
"monaco-editor": "^0.46.0",
|
||||
"monaco-yaml": "^5.1.1",
|
||||
"oidc-client-ts": "^2.2.4",
|
||||
"react": "^18.2.0",
|
||||
"react-device-detect": "^2.2.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-oidc-context": "^2.2.2",
|
||||
"react-router-dom": "^6.14.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-use-websocket": "^4.7.0",
|
||||
|
||||
72
src/App.tsx
72
src/App.tsx
@ -1,19 +1,15 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { hasAuthParams, useAuth } from 'react-oidc-context';
|
||||
import CenterLoader from './shared/components/loaders/CenterLoader';
|
||||
import { ColorScheme, ColorSchemeProvider, MantineProvider } from '@mantine/core';
|
||||
import { useColorScheme } from '@mantine/hooks';
|
||||
import { getCookie, setCookie } from 'cookies-next';
|
||||
import AppBody from './AppBody';
|
||||
import Forbidden from './pages/403';
|
||||
import { ModalsProvider } from '@mantine/modals';
|
||||
import { Notifications } from '@mantine/notifications';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import RetryErrorPage from './pages/RetryErrorPage';
|
||||
import { keycloakConfig } from '.';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { ModalsProvider } from '@mantine/modals';
|
||||
import { getCookie, setCookie } from 'cookies-next';
|
||||
import { useState } from 'react';
|
||||
import AppBody from './AppBody';
|
||||
import { FfprobeModal } from './shared/components/modal.windows/FfprobeModal';
|
||||
import { VaInfoModal } from './shared/components/modal.windows/VaInfoModal';
|
||||
import { useRealmAccessRoles } from './hooks/useRealmAccessRoles';
|
||||
import { useAdminRole } from './hooks/useAdminRole';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
@ -35,70 +31,14 @@ declare module '@mantine/modals' {
|
||||
}
|
||||
|
||||
function App() {
|
||||
const maxErrorAuthCounts = 2
|
||||
const systemColorScheme = useColorScheme()
|
||||
const [colorScheme, setColorScheme] = useState<ColorScheme>(getCookie('mantine-color-scheme') as ColorScheme || systemColorScheme)
|
||||
const [authErrorCounter, setAuthErrorCounter] = useState(0)
|
||||
const toggleColorScheme = (value?: ColorScheme) => {
|
||||
const nextColorScheme = value || (colorScheme === 'dark' ? 'light' : 'dark');
|
||||
setColorScheme(nextColorScheme)
|
||||
setCookie('mantine-color-scheme', nextColorScheme, { maxAge: 60 * 60 * 24 * 30 });
|
||||
}
|
||||
|
||||
const auth = useAuth()
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
// automatically sign-in
|
||||
useEffect(() => {
|
||||
if (!hasAuthParams() &&
|
||||
!auth.isAuthenticated && !auth.activeNavigator && !auth.isLoading && authErrorCounter < maxErrorAuthCounts) {
|
||||
console.error('Not authenticated! Redirect! ErrorCounter', authErrorCounter)
|
||||
setAuthErrorCounter(prevCount => prevCount + 1)
|
||||
auth.signinRedirect()
|
||||
}
|
||||
}, [auth, auth.isAuthenticated, auth.activeNavigator, auth.isLoading, auth.signinRedirect, authErrorCounter])
|
||||
|
||||
|
||||
if (auth.activeNavigator || auth.isLoading) {
|
||||
return <CenterLoader />
|
||||
}
|
||||
|
||||
if (authErrorCounter > maxErrorAuthCounts) {
|
||||
console.error('maxErrorAuthCounts authority', keycloakConfig.authority)
|
||||
console.error('maxErrorAuthCounts client_id', keycloakConfig.client_id)
|
||||
console.error('maxErrorAuthCounts redirect_uri', keycloakConfig.redirect_uri)
|
||||
return <RetryErrorPage backVisible={false} mainVisible={false} onRetry={() => auth.signinRedirect()} />
|
||||
}
|
||||
|
||||
if (hasAuthParams()) {
|
||||
const urlParams = new URLSearchParams(location.search);
|
||||
urlParams.delete('state');
|
||||
urlParams.delete('session_state');
|
||||
urlParams.delete('code');
|
||||
urlParams.delete('iss');
|
||||
navigate(`${location.pathname}${urlParams.toString() ? '?' + urlParams.toString() : ''}`, { replace: true })
|
||||
}
|
||||
|
||||
|
||||
if (!auth.isAuthenticated && !auth.isLoading && authErrorCounter < maxErrorAuthCounts) {
|
||||
if (hasAuthParams()) {
|
||||
console.warn('Not authenticated, isAuthenticated:', auth.isAuthenticated)
|
||||
console.warn('Not authenticated, isLoading:', auth.isLoading)
|
||||
return <RetryErrorPage backVisible={false} mainVisible={false} onRetry={() => auth.signinRedirect()} />
|
||||
} else {
|
||||
console.error('Not authenticated! Redirect! Error Counter:', authErrorCounter)
|
||||
setAuthErrorCounter(prevCount => prevCount + 1);
|
||||
auth.signinRedirect()
|
||||
}
|
||||
}
|
||||
|
||||
if ((!hasAuthParams() && !auth.isAuthenticated && !auth.isLoading) || auth.error) {
|
||||
setAuthErrorCounter(prevCount => prevCount + 1)
|
||||
console.error(`auth.error:`, auth.error)
|
||||
return <Forbidden />
|
||||
}
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<div className="App">
|
||||
|
||||
@ -8,6 +8,8 @@ import { routesPath } from './router/routes.path';
|
||||
import SideBar from './shared/components/SideBar';
|
||||
import { isProduction } from './shared/env.const';
|
||||
import { HeaderAction } from './widgets/header/HeaderAction';
|
||||
import { useAdminRole } from "./hooks/useAdminRole";
|
||||
import { useRealmAccessRoles } from "./hooks/useRealmAccessRoles";
|
||||
|
||||
const AppBody = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@ -3,29 +3,30 @@ import { frigateQueryKeys, frigateApi } from "../services/frigate.proxy/frigate.
|
||||
import { useRealmAccessRoles } from "./useRealmAccessRoles";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export interface AdminRole {
|
||||
isLoading: boolean
|
||||
isAdmin: boolean
|
||||
}
|
||||
|
||||
export const useAdminRole = () => {
|
||||
const { data: adminConfig, isError, isFetching } = useQuery({
|
||||
export const useAdminRole = (): AdminRole => {
|
||||
const { data: adminConfig, isError, isLoading } = useQuery({
|
||||
queryKey: [frigateQueryKeys.getAdminRole],
|
||||
queryFn: frigateApi.getAdminRole,
|
||||
staleTime: 1000 * 60 * 60,
|
||||
gcTime: 1000 * 60 * 60 * 24,
|
||||
staleTime: 1000 * 60 * 60,
|
||||
gcTime: 1000 * 60 * 60 * 24,
|
||||
})
|
||||
|
||||
const roles = useRealmAccessRoles()
|
||||
const [initialized, setInitialized] = useState(false)
|
||||
const isLoading = isFetching || roles === undefined
|
||||
const [isAdmin, setIsAdmin] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
setInitialized(true);
|
||||
if (adminConfig) {
|
||||
const checkAdmin = roles.some(role => role === adminConfig.value)
|
||||
setIsAdmin(checkAdmin)
|
||||
} else {
|
||||
setIsAdmin(false)
|
||||
}
|
||||
}, [isLoading]);
|
||||
}, [roles, adminConfig, isLoading])
|
||||
|
||||
if (!initialized || isLoading) return { isAdmin: undefined, isLoading: true }
|
||||
if (isError) return { isAdmin: false, isError: true, isLoading: false }
|
||||
if (!adminConfig) return { isAdmin: true, isLoading: false }
|
||||
if (adminConfig && !adminConfig.value) return { isAdmin: true, isLoading: false }
|
||||
const isAdmin = roles.some(role => role === adminConfig.value)
|
||||
return { isAdmin, isLoading: false }
|
||||
return { isLoading, isAdmin }
|
||||
}
|
||||
@ -1,30 +1,36 @@
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useAuth } from "react-oidc-context";
|
||||
|
||||
interface CustomJwtPayload {
|
||||
realm_access?: {
|
||||
roles: string[];
|
||||
};
|
||||
}
|
||||
import { isProduction } from "../shared/env.const";
|
||||
import { useKeycloak } from "@react-keycloak/web";
|
||||
|
||||
export const useRealmAccessRoles = () => {
|
||||
const { user } = useAuth();
|
||||
const [roles, setRoles] = useState<string[]>([]);
|
||||
const { keycloak } = useKeycloak()
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
try {
|
||||
const decoded = jwtDecode<CustomJwtPayload>(user.access_token);
|
||||
const realmAccess = decoded.realm_access;
|
||||
if (realmAccess && realmAccess.roles) {
|
||||
setRoles(realmAccess.roles);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error decoding token:", error);
|
||||
const updateRoles = () => {
|
||||
const tokenRoles = keycloak.tokenParsed?.realm_access?.roles;
|
||||
if (!isProduction) console.log(`tokenRoles:`, tokenRoles);
|
||||
if (tokenRoles) {
|
||||
setRoles(tokenRoles);
|
||||
} else {
|
||||
setRoles([])
|
||||
}
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
updateRoles()
|
||||
|
||||
return roles;
|
||||
};
|
||||
keycloak.onAuthSuccess = () => {
|
||||
updateRoles()
|
||||
}
|
||||
keycloak.onAuthRefreshSuccess = () => {
|
||||
updateRoles()
|
||||
}
|
||||
|
||||
return () => {
|
||||
keycloak.onAuthSuccess = undefined
|
||||
keycloak.onAuthRefreshSuccess = undefined
|
||||
}
|
||||
}, [keycloak, keycloak.onAuthSuccess, keycloak.onAuthRefreshSuccess ])
|
||||
|
||||
return roles
|
||||
}
|
||||
@ -3,10 +3,11 @@ import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import RootStore from './shared/stores/root.store';
|
||||
import { AuthProvider, AuthProviderProps } from 'react-oidc-context';
|
||||
import { isProduction, oidpSettings } from './shared/env.const';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import './services/i18n';
|
||||
import { ReactKeycloakProvider } from '@react-keycloak/web';
|
||||
import keycloak from './services/keycloak-config';
|
||||
import CenterLoader from './shared/components/loaders/CenterLoader';
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
@ -14,39 +15,35 @@ const root = ReactDOM.createRoot(
|
||||
|
||||
export const hostURL = new URL(window.location.href)
|
||||
|
||||
export const keycloakConfig: AuthProviderProps = {
|
||||
authority: oidpSettings.server,
|
||||
client_id: oidpSettings.clientId,
|
||||
redirect_uri: hostURL.toString(),
|
||||
onSigninCallback: () => {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const params = currentUrl.searchParams;
|
||||
params.delete('state');
|
||||
params.delete('session_state');
|
||||
params.delete('code');
|
||||
params.delete('iss');
|
||||
const newUrl = `${window.location.pathname}${params.toString() ? '?' + params.toString() : ''}`
|
||||
window.history.replaceState({}, document.title, newUrl)
|
||||
}
|
||||
}
|
||||
|
||||
const rootStore = new RootStore()
|
||||
export const Context = createContext<RootStore>(rootStore)
|
||||
|
||||
if (!isProduction) {
|
||||
console.log('keycloakConfig.authority', keycloakConfig.authority)
|
||||
console.log('keycloakConfig.client_id', keycloakConfig.client_id)
|
||||
console.log('keycloakConfig.redirect_uri', keycloakConfig.redirect_uri)
|
||||
}
|
||||
const eventLogger = (event: string, error?: any) => {
|
||||
console.log('onKeycloakEvent', event, error);
|
||||
};
|
||||
|
||||
const tokenLogger = (tokens: any) => {
|
||||
console.log('onKeycloakTokens', tokens);
|
||||
};
|
||||
|
||||
root.render(
|
||||
<Context.Provider value={rootStore}>
|
||||
<AuthProvider {...keycloakConfig}>
|
||||
<ReactKeycloakProvider
|
||||
authClient={keycloak}
|
||||
LoadingComponent={<CenterLoader />}
|
||||
onEvent={eventLogger}
|
||||
onTokens={tokenLogger}
|
||||
initOptions={{
|
||||
onLoad: 'login-required',
|
||||
checkLoginIframe: false
|
||||
}}
|
||||
>
|
||||
<Context.Provider value={rootStore}>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</AuthProvider>
|
||||
</Context.Provider>
|
||||
</Context.Provider>
|
||||
</ReactKeycloakProvider>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
||||
@ -5,9 +5,11 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Context } from '..';
|
||||
import { routesPath } from '../router/routes.path';
|
||||
import CogWheelWithText from '../shared/components/loaders/CogWheelWithText';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const Forbidden = () => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const executed = useRef(false)
|
||||
const { sideBarsStore } = useContext(Context)
|
||||
|
||||
@ -21,7 +23,7 @@ const Forbidden = () => {
|
||||
}, [sideBarsStore])
|
||||
|
||||
const handleGoToMain = () => {
|
||||
window.location.replace(routesPath.MAIN_PATH)
|
||||
navigate(routesPath.MAIN_PATH)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -8,19 +8,10 @@ import {
|
||||
import { FrigateConfig } from "../../types/frigateConfig";
|
||||
import { RecordSummary } from "../../types/record";
|
||||
import { EventFrigate } from "../../types/event";
|
||||
import { keycloakConfig } from "../..";
|
||||
import { getResolvedTimeZone } from "../../shared/utils/dateUtil";
|
||||
import { FrigateStats, GetFfprobe, GetHostStorage, GetVaInfo } from "../../types/frigateStats";
|
||||
import { hostname } from "os";
|
||||
import { PostSaveConfig, SaveOption } from "../../types/saveConfig";
|
||||
|
||||
|
||||
export const getToken = (): string | undefined => {
|
||||
const key = `oidc.user:${keycloakConfig.authority}:${keycloakConfig.client_id}`;
|
||||
const stored = sessionStorage.getItem(key);
|
||||
const storedObject = stored ? JSON.parse(stored) : null;
|
||||
return storedObject?.access_token;
|
||||
}
|
||||
import keycloak from "../keycloak-config";
|
||||
|
||||
const instanceApi = axios.create({
|
||||
baseURL: proxyURL.toString(),
|
||||
@ -29,9 +20,9 @@ const instanceApi = axios.create({
|
||||
|
||||
instanceApi.interceptors.request.use(
|
||||
config => {
|
||||
const token = getToken();
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
const accessToken = keycloak.token;
|
||||
if (accessToken) {
|
||||
config.headers.Authorization = `Bearer ${accessToken}`
|
||||
}
|
||||
return config;
|
||||
},
|
||||
|
||||
12
src/services/keycloak-config.ts
Normal file
12
src/services/keycloak-config.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import Keycloak from "keycloak-js";
|
||||
import { oidpSettings } from "../shared/env.const";
|
||||
|
||||
const keycloakConfig = {
|
||||
url: oidpSettings.server,
|
||||
realm: oidpSettings.realm,
|
||||
clientId: oidpSettings.clientId,
|
||||
};
|
||||
|
||||
const keycloak = new Keycloak(keycloakConfig);
|
||||
|
||||
export default keycloak;
|
||||
@ -2,10 +2,9 @@ import { Avatar, Button, Flex, Group, Menu, Text } from "@mantine/core";
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAuth } from 'react-oidc-context';
|
||||
import { keycloakConfig } from '../..';
|
||||
import { dimensions } from '../dimensions/dimensions';
|
||||
import ColorSchemeToggle from './buttons/ColorSchemeToggle';
|
||||
import keycloak from "../../services/keycloak-config";
|
||||
|
||||
interface UserMenuProps {
|
||||
user: { name: string; image: string }
|
||||
@ -20,15 +19,10 @@ const UserMenu = ({ user }: UserMenuProps) => {
|
||||
{ lng: 'ru', name: 'Rus' },
|
||||
]
|
||||
|
||||
const auth = useAuth()
|
||||
const isMiddleScreen = useMediaQuery(dimensions.middleScreenSize)
|
||||
|
||||
|
||||
|
||||
const handleLogout = async () => {
|
||||
await auth.removeUser()
|
||||
const id_token_hint = auth.user?.id_token
|
||||
await auth.signoutRedirect({ post_logout_redirect_uri: keycloakConfig.redirect_uri, id_token_hint: id_token_hint })
|
||||
keycloak.logout({ redirectUri: window.location.origin })
|
||||
}
|
||||
|
||||
const handleChangeLanguage = async (lng: string) => {
|
||||
|
||||
@ -39,7 +39,7 @@ const DrawerMenu = ({
|
||||
links
|
||||
}: DrawerMenuProps) => {
|
||||
const navigate = useNavigate()
|
||||
const { isAdmin } = useAdminRole()
|
||||
const isAdmin = useAdminRole()
|
||||
|
||||
|
||||
const { classes } = useStyles();
|
||||
|
||||
@ -2,14 +2,15 @@ import React, { useRef, useEffect } from 'react';
|
||||
import videojs from 'video.js';
|
||||
import Player from 'video.js/dist/types/player';
|
||||
import 'video.js/dist/video-js.css'
|
||||
import { getToken } from '../../../services/frigate.proxy/frigate.api';
|
||||
import { isProduction } from '../../env.const';
|
||||
import { useKeycloak } from '@react-keycloak/web';
|
||||
|
||||
interface VideoPlayerProps {
|
||||
videoUrl: string
|
||||
}
|
||||
|
||||
const VideoPlayer = ({ videoUrl }: VideoPlayerProps) => {
|
||||
const { keycloak } = useKeycloak()
|
||||
const executed = useRef(false)
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const playerRef = useRef<Player | null>(null);
|
||||
@ -20,7 +21,7 @@ const VideoPlayer = ({ videoUrl }: VideoPlayerProps) => {
|
||||
videojs.Vhs.xhr.beforeRequest = function (options: any) {
|
||||
options.headers = {
|
||||
...options.headers,
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
Authorization: `Bearer ${keycloak.token}`,
|
||||
};
|
||||
return options;
|
||||
};
|
||||
|
||||
@ -12,7 +12,11 @@ const oidpServer = isProduction ? window.env?.OPENID_SERVER : process.env.REACT_
|
||||
const oidpServerParsed= z.string().url().safeParse(oidpServer)
|
||||
if (!oidpServerParsed.success) throw Error(`OPENID_SERVER must be string and URL. OPENID_SERVER:${oidpServer}`)
|
||||
const oidpClientId = isProduction ? window.env?.CLIENT_ID : process.env.REACT_APP_CLIENT_ID
|
||||
const oidpRealm = isProduction ? window.env?.REALM : process.env.REACT_APP_REALM
|
||||
const parsedRealm = z.string().safeParse(oidpRealm)
|
||||
if (!parsedRealm.success) throw Error(`REALM must be string and exist. REALM:${oidpRealm}`)
|
||||
export const oidpSettings = {
|
||||
server: oidpServer || '',
|
||||
clientId: oidpClientId || '',
|
||||
realm: oidpRealm || '',
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
import { makeAutoObservable, runInAction } from "mobx"
|
||||
import { User } from "oidc-client-ts";
|
||||
import { Resource } from "../utils/resource"
|
||||
import { sleep } from "../utils/async.sleep";
|
||||
import { z } from 'zod'
|
||||
import { keycloakConfig } from "../..";
|
||||
|
||||
|
||||
export interface UserServer {
|
||||
@ -71,20 +69,20 @@ export class UserStore {
|
||||
}
|
||||
|
||||
getSessionStorage() {
|
||||
const oidcStorage = sessionStorage.getItem(`oidc.user:${keycloakConfig.authority}:${keycloakConfig.client_id}`)
|
||||
if (!oidcStorage) {
|
||||
return undefined;
|
||||
}
|
||||
// const oidcStorage = sessionStorage.getItem(`oidc.user:${keycloakConfig.authority}:${keycloakConfig.client_id}`)
|
||||
// if (!oidcStorage) {
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
return User.fromStorageString(oidcStorage)
|
||||
// return User.fromStorageString(oidcStorage)
|
||||
}
|
||||
|
||||
getUser() {
|
||||
return this.getSessionStorage()?.profile
|
||||
// return this.getSessionStorage()?.profile
|
||||
}
|
||||
|
||||
getAccessToken() {
|
||||
return this.getSessionStorage()?.access_token
|
||||
// return this.getSessionStorage()?.access_token
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { Button, Container, Flex, Group, Header, Menu, createStyles, rem } from "@mantine/core";
|
||||
import { useAuth } from 'react-oidc-context';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAdminRole } from "../../hooks/useAdminRole";
|
||||
import { routesPath } from "../../router/routes.path";
|
||||
@ -7,6 +6,7 @@ import UserMenu from '../../shared/components/UserMenu';
|
||||
import ColorSchemeToggle from "../../shared/components/buttons/ColorSchemeToggle";
|
||||
import Logo from "../../shared/components/images/LogoImage";
|
||||
import DrawerMenu from "../../shared/components/menu/DrawerMenu";
|
||||
import keycloak from "../../services/keycloak-config";
|
||||
|
||||
const HEADER_HEIGHT = rem(60)
|
||||
|
||||
@ -61,7 +61,6 @@ export interface HeaderActionProps {
|
||||
export const HeaderAction = ({ links }: HeaderActionProps) => {
|
||||
const { classes } = useStyles();
|
||||
const navigate = useNavigate()
|
||||
const auth = useAuth()
|
||||
const { isAdmin } = useAdminRole()
|
||||
|
||||
const handleNavigate = (link: string) => {
|
||||
@ -78,6 +77,8 @@ export const HeaderAction = ({ links }: HeaderActionProps) => {
|
||||
</Menu>
|
||||
)
|
||||
|
||||
const userName = keycloak.tokenParsed?.preferred_username + (isAdmin ? ' (admin)' : '')
|
||||
|
||||
return (
|
||||
<Header height={HEADER_HEIGHT} sx={{ borderBottom: 0 }}>
|
||||
<Container className={classes.inner} fluid>
|
||||
@ -93,7 +94,7 @@ export const HeaderAction = ({ links }: HeaderActionProps) => {
|
||||
</Container>
|
||||
<Group position="right">
|
||||
<ColorSchemeToggle className={classes.colorToggle} />
|
||||
<UserMenu user={{ name: auth.user?.profile.preferred_username || "", image: "" }} />
|
||||
<UserMenu user={{ name: userName, image: "" }} />
|
||||
</Group>
|
||||
</Container>
|
||||
</Header >
|
||||
|
||||
66
yarn.lock
66
yarn.lock
@ -1129,6 +1129,13 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/runtime@^7.9.0":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.1.tgz#431f9a794d173b53720e69a6464abc6f0e2a5c57"
|
||||
integrity sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/template@^7.22.15", "@babel/template@^7.24.0", "@babel/template@^7.3.3":
|
||||
version "7.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50"
|
||||
@ -1979,6 +1986,22 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@react-keycloak/core@^3.2.0":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-keycloak/core/-/core-3.2.0.tgz#e46f1951b0d7873f7f2fcd73dd0c270cb0b18db8"
|
||||
integrity sha512-1yzU7gQzs+6E1v6hGqxy0Q+kpMHg9sEcke2yxZR29WoU8KNE8E50xS6UbI8N7rWsgyYw8r9W1cUPCOF48MYjzw==
|
||||
dependencies:
|
||||
react-fast-compare "^3.2.0"
|
||||
|
||||
"@react-keycloak/web@^3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-keycloak/web/-/web-3.4.0.tgz#725d96fab8e5fa47faff9615cc08574e5dff2222"
|
||||
integrity sha512-yKKSCyqBtn7dt+VckYOW1IM5NW999pPkxDZOXqJ6dfXPXstYhOQCkTZqh8l7UL14PkpsoaHDh7hSJH8whah01g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.0"
|
||||
"@react-keycloak/core" "^3.2.0"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
|
||||
"@remix-run/router@1.15.2":
|
||||
version "1.15.2"
|
||||
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.15.2.tgz#35726510d332ba5349c6398d13259d5da184553d"
|
||||
@ -3978,11 +4001,6 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
crypto-js@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
|
||||
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
|
||||
|
||||
crypto-random-string@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
||||
@ -5649,7 +5667,7 @@ he@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
hoist-non-react-statics@^3.3.1:
|
||||
hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
@ -6818,6 +6836,11 @@ jiti@^1.19.1:
|
||||
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d"
|
||||
integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==
|
||||
|
||||
js-sha256@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.11.0.tgz#256a921d9292f7fe98905face82e367abaca9576"
|
||||
integrity sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -6961,16 +6984,19 @@ jsonpointer@^5.0.0:
|
||||
object.assign "^4.1.4"
|
||||
object.values "^1.1.6"
|
||||
|
||||
jwt-decode@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
|
||||
integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==
|
||||
|
||||
jwt-decode@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-4.0.0.tgz#2270352425fd413785b2faf11f6e755c5151bd4b"
|
||||
integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==
|
||||
|
||||
keycloak-js@^24.0.1:
|
||||
version "24.0.1"
|
||||
resolved "https://registry.yarnpkg.com/keycloak-js/-/keycloak-js-24.0.1.tgz#8de412bb2b914457b0dc9387a4a824bd0e8d171d"
|
||||
integrity sha512-leV4mlpa0dqYUXTAuq1ufUfk8DOSBCembjQwMwzYrM6xfHSKpcZMxviTWXqro52LMSsYAnivSKVNEvBkLzi7Eg==
|
||||
dependencies:
|
||||
js-sha256 "^0.11.0"
|
||||
jwt-decode "^4.0.0"
|
||||
|
||||
keycode@2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
|
||||
@ -7645,14 +7671,6 @@ obuf@^1.0.0, obuf@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
|
||||
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
|
||||
|
||||
oidc-client-ts@^2.2.4:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/oidc-client-ts/-/oidc-client-ts-2.4.0.tgz#764c8a33de542026e2798de9849ce8049047d7e5"
|
||||
integrity sha512-WijhkTrlXK2VvgGoakWJiBdfIsVGz6CFzgjNNqZU1hPKV2kyeEaJgLs7RwuiSp2WhLfWBQuLvr2SxVlZnk3N1w==
|
||||
dependencies:
|
||||
crypto-js "^4.2.0"
|
||||
jwt-decode "^3.1.2"
|
||||
|
||||
on-finished@2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
|
||||
@ -8685,6 +8703,11 @@ react-error-overlay@^6.0.11:
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
|
||||
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
|
||||
|
||||
react-fast-compare@^3.2.0:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49"
|
||||
integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==
|
||||
|
||||
react-i18next@^14.1.0:
|
||||
version "14.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-14.1.0.tgz#44da74fbffd416f5d0c5307ef31735cf10cc91d9"
|
||||
@ -8708,11 +8731,6 @@ react-is@^18.0.0:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||
|
||||
react-oidc-context@^2.2.2:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-oidc-context/-/react-oidc-context-2.3.1.tgz#04eea5aaea972af1a49de6b8d5ff103b9daa0e73"
|
||||
integrity sha512-WdhmEU6odNzMk9pvOScxUkf6/1aduiI/nQryr7+iCl2VDnYLASDTIV/zy58KuK4VXG3fBaRKukc/mRpMjF9a3Q==
|
||||
|
||||
react-refresh@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user