mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
108 lines
2.9 KiB
TypeScript
108 lines
2.9 KiB
TypeScript
'use client';
|
|
|
|
import { ReactNode, useEffect } from 'react';
|
|
import useSWR from 'swr';
|
|
|
|
import { useAuth } from '@/services/hooks/useAuth';
|
|
import { httpClientFetcher, SWRHttpKey } from '@/services/http/client';
|
|
import { AuthApi } from '@/services/api/auth';
|
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
|
import { BaseApiResponse, GetMeResponse } from '@/types/api/api-general';
|
|
import { AxiosError } from 'axios';
|
|
import { redirectToSSO } from '@/lib/auth-helper';
|
|
|
|
interface RequireAuthProps {
|
|
children?: ReactNode;
|
|
}
|
|
|
|
const RequireAuth = ({ children }: RequireAuthProps) => {
|
|
const { user, setUser, setIsLoadingUser } = useAuth();
|
|
|
|
const {
|
|
data: userResponse,
|
|
isLoading: isLoadingUserResponse,
|
|
error: userErrorResponse,
|
|
} = useSWR<
|
|
GetMeResponse & { ok?: boolean },
|
|
AxiosError<BaseApiResponse>,
|
|
SWRHttpKey
|
|
>('/sso/userinfo', httpClientFetcher, {
|
|
shouldRetryOnError: false,
|
|
|
|
// refresh every 12 minutes
|
|
refreshInterval: 12 * 60 * 1000,
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (isResponseSuccess(userResponse)) {
|
|
setUser(userResponse.data);
|
|
}
|
|
}, [userResponse, setUser]);
|
|
|
|
// Explicitly handle 401 redirect from the component level
|
|
useEffect(() => {
|
|
if (
|
|
isResponseError(userResponse) &&
|
|
userErrorResponse?.response?.status === 401
|
|
) {
|
|
// Clear cache to prevent stale data from rendering children
|
|
// mutate('/sso/userinfo', undefined, { revalidate: false }); // Optional: if using global mutate
|
|
setUser(undefined);
|
|
redirectToSSO();
|
|
}
|
|
}, [userErrorResponse, setUser, userResponse]);
|
|
|
|
useEffect(() => {
|
|
setIsLoadingUser(isLoadingUserResponse);
|
|
}, [isLoadingUserResponse]);
|
|
|
|
useEffect(() => {
|
|
const interval = setInterval(
|
|
async () => {
|
|
await AuthApi.refresh();
|
|
},
|
|
12 * 60 * 1000
|
|
);
|
|
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const refreshUserSession = async () => {
|
|
await AuthApi.refresh();
|
|
};
|
|
|
|
refreshUserSession();
|
|
}, []);
|
|
|
|
if (
|
|
(isLoadingUserResponse && !userResponse && !userErrorResponse) ||
|
|
(!userResponse && !userErrorResponse)
|
|
) {
|
|
return (
|
|
<div className='w-full flex flex-row justify-center items-center p-4'>
|
|
<span className='loading loading-spinner loading-xl' />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!isLoadingUserResponse && userErrorResponse) {
|
|
return (
|
|
<div className='w-full h-screen flex flex-col justify-center items-center gap-4'>
|
|
<h2 className='text-2xl font-bold text-error'>Authentication Failed</h2>
|
|
<p className='text-gray-600'>
|
|
Please try refreshing the page or contact support if the problem
|
|
persists.
|
|
</p>
|
|
<button className='btn btn-primary' onClick={() => redirectToSSO()}>
|
|
Retry
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return <>{isResponseSuccess(userResponse) && user && children}</>;
|
|
};
|
|
|
|
export default RequireAuth;
|