fix infinity auth redirecting

add validation oidp server
This commit is contained in:
NlightN22 2024-03-01 18:21:07 +07:00
parent 811bc5bac7
commit a5e11f067c
6 changed files with 61 additions and 22 deletions

View File

@ -14,7 +14,7 @@ services:
REACT_APP_OPENID_SERVER: https://server:port/realms/your-realm
REACT_APP_CLIENT_ID: frontend-client
ports:
- 5173:80 # set your port here
- 80:80 # set your port here
```
- run:
```bash

View File

@ -10,4 +10,4 @@ services:
REACT_APP_OPENID_SERVER: https://server:port/realms/your-realm
REACT_APP_CLIENT_ID: frontend-client
ports:
- 5173:80 # set your port here
- 80:80 # set your port here

View File

@ -4,11 +4,12 @@ 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 { BrowserRouter } from 'react-router-dom';
import AppBody from './AppBody';
import Forbidden from './pages/403';
import { Notifications } from '@mantine/notifications';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import RetryErrorPage from './pages/RetryErrorPage';
import { keycloakConfig } from '.';
const queryClient = new QueryClient({
@ -20,8 +21,10 @@ const queryClient = new QueryClient({
})
function App() {
const maxErrorAuthConts = 10
const systemColorScheme = useColorScheme()
const [colorScheme, setColorScheme] = useState<ColorScheme>(getCookie('mantine-color-scheme') as ColorScheme || systemColorScheme);
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)
@ -33,15 +36,32 @@ function App() {
// automatically sign-in
useEffect(() => {
if (!hasAuthParams() &&
!auth.isAuthenticated && !auth.activeNavigator && !auth.isLoading) {
auth.signinRedirect();
!auth.isAuthenticated && !auth.activeNavigator && !auth.isLoading && authErrorCounter < maxErrorAuthConts) {
console.log('not authenticated! redirect!')
auth.signinRedirect()
}
}, [auth, auth.isAuthenticated, auth.activeNavigator, auth.isLoading, auth.signinRedirect]);
}, [auth, auth.isAuthenticated, auth.activeNavigator, auth.isLoading, auth.signinRedirect])
if (auth.activeNavigator || auth.isLoading) {
return <CenterLoader />
}
if ((!auth.isAuthenticated && !auth.isLoading) || auth.error) {
if (authErrorCounter > maxErrorAuthConts) {
console.log('maxErrorAuthConts authority', keycloakConfig.authority)
console.log('maxErrorAuthConts client_id', keycloakConfig.client_id)
console.log('maxErrorAuthConts redirect_uri', keycloakConfig.redirect_uri)
return <RetryErrorPage backVisible={false} mainVisible={false} onRetry={() => auth.signinRedirect()} />
}
if (!auth.isAuthenticated && !auth.isLoading && authErrorCounter < maxErrorAuthConts) {
console.log('not authenticated! redirect!')
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 />
}
@ -72,10 +92,8 @@ function App() {
}
}}
>
<BrowserRouter>
<Notifications />
<AppBody />
</BrowserRouter>
<Notifications />
<AppBody />
</MantineProvider >
</ColorSchemeProvider>
</div>

View File

@ -4,7 +4,8 @@ import App from './App';
import reportWebVitals from './reportWebVitals';
import RootStore from './shared/stores/root.store';
import { AuthProvider, AuthProviderProps } from 'react-oidc-context';
import { oidpSettings } from './shared/env.const';
import { isProduction, oidpSettings } from './shared/env.const';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
@ -21,14 +22,22 @@ export const keycloakConfig: AuthProviderProps = {
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)
}
root.render(
<Context.Provider value={rootStore}>
<AuthProvider {...keycloakConfig}>
<Context.Provider value={rootStore}>
<AuthProvider {...keycloakConfig}>
<BrowserRouter>
{/* <React.StrictMode> */}
<App />
<App />
{/* </React.StrictMode> */}
</AuthProvider>
</Context.Provider>
</BrowserRouter>
</AuthProvider>
</Context.Provider>
);
// If you want to start measuring performance in your app, pass a function

View File

@ -8,10 +8,18 @@ import { Context } from '..';
import { observer } from 'mobx-react-lite';
interface RetryErrorPageProps {
repeatVisible?: boolean
backVisible?: boolean
mainVisible?: boolean
onRetry?: () => void
}
const RetryErrorPage = ({ onRetry }: RetryErrorPageProps) => {
const RetryErrorPage = ({
repeatVisible = true,
backVisible = true,
mainVisible = true,
onRetry
}: RetryErrorPageProps) => {
const executed = useRef(false)
const navigate = useNavigate()
@ -45,9 +53,9 @@ const RetryErrorPage = ({ onRetry }: RetryErrorPageProps) => {
{ExclamationCogWheel}
<Text fz='lg' fw={700}>{strings.youCanRetryOrGoToMain}</Text>
<Flex>
<Button ml='1rem' onClick={handleRetry}>{strings.retry}</Button>
<Button ml='1rem' onClick={handleGoBack}>{strings.back}</Button>
<Button ml='1rem' onClick={handleGoToMain}>{strings.goToMainPage}</Button>
{repeatVisible ? <Button ml='1rem' onClick={handleRetry}>{strings.retry}</Button> : null}
{ backVisible ? <Button ml='1rem' onClick={handleGoBack}>{strings.back}</Button> : null }
{ mainVisible ? <Button ml='1rem' onClick={handleGoToMain}>{strings.goToMainPage}</Button> : null }
</Flex>
</Flex>
);

View File

@ -1,3 +1,5 @@
import { z } from "zod"
export const appMode = process.env.NODE_ENV
export const isProduction = appMode === "production"
export const host = isProduction ? window.env?.REACT_APP_HOST : process.env.HOST
@ -8,6 +10,8 @@ const proxy = isProduction ? window.env?.REACT_APP_FRIGATE_PROXY : process.env.
export const proxyURL = new URL(proxy || '')
const oidpServer = isProduction ? window.env?.REACT_APP_OPENID_SERVER : process.env.REACT_APP_OPENID_SERVER
const oidpServerParsed= z.string().url().safeParse(oidpServer)
if (!oidpServerParsed.success) throw Error('REACT_APP_OPENID_SERVER must be string and URL')
const oidpClientId = isProduction ? window.env?.REACT_APP_CLIENT_ID : process.env.REACT_APP_CLIENT_ID
export const oidpSettings = {
server: oidpServer || '',