Mnai
Mnai

Reputation: 507

Supabase Authentication Redirection Flicker in Next.js using HOC w/ useSessionContext

I am using Supabase for authentication in my web application. I've noticed that when I'm already logged in and try to navigate to the login page for testing, it briefly shows the login page before redirecting to the correct protected route. This happens despite having an isLoading state set up using useSessionContext().

I expected it to display the CircularProgress while isLoading is true, then, once isLoading is false and session is true (indicating I'm already logged in), it would directly redirect me without showing the login page. However, in practice, I see the login page briefly before the redirection happens.

In development mode, this login page is visible for a few seconds before redirecting. In production mode (using npm start), it's more of a flicker. Why is this happening and how can I make it display only the CircularProgress while loading, then directly redirect if I'm already logged in?

HOC component

import CircularProgress from '@mui/material/CircularProgress';
import { useSessionContext } from '@supabase/auth-helpers-react';
import { useRouter } from 'next/router';
import { FC, useEffect } from 'react';
import ErrorIcon from '@mui/icons-material/Error';
import { Typography } from '@mui/material';

export const withAuth = <T extends Record<string, unknown>>(Component: FC<T>, redirectLink = '/') => {
    const AuthenticatedComponent: FC<T> = (props) => {
        const router = useRouter();
        const { isLoading, session, error } = useSessionContext();

        useEffect(() => {
            const checkAuth = async () => {
                if (session) {
                    await router.replace(redirectLink);
                }
            };
            checkAuth();
        }, [session, router]);

        if (isLoading) return <CircularProgress color='primary' size={'2rem'} />;

        if (error)
            return (
                <>
                    <ErrorIcon color='error' fontSize='large' />
                    <Typography variant='body1'>We are experiencing technical difficulties. Please try again later.</Typography>
                </>
            );

        return <Component {...(props as T)} />;
    };

    return AuthenticatedComponent;
};

Login Page

const LoginPage = () => {
    const supabaseClient = useSupabaseClient();
    return (
        <Box sx={{ width: '50%', padding: '50px' }}>
            <Auth redirectTo='/' appearance={{ theme: ThemeSupa }} supabaseClient={supabaseClient} providers={['google']} socialLayout='horizontal' />
        </Box>
    );
};

export default withAuth(LoginPage);

Upvotes: 2

Views: 536

Answers (1)

Mnai
Mnai

Reputation: 507

After some investigation, I've realized the brief display of the login page before redirecting is due to both the session check and the redirect operation being asynchronous. As a result, there is a small delay where the login page gets rendered before the redirect action takes place.

Initially, my condition for displaying the CircularProgress was only when isLoading was true, i.e., while the session check was ongoing. However, it seems this wasn't enough to prevent the brief display of the login page, as the redirect operation also needs time to complete.

I've found a solution to this problem by showing the loading spinner not just during the session check, but also when a session is present and a redirect is about to occur.

I updated my code like this:

if (isLoading || session) {
    return <CircularProgress color='primary' size={'2rem'} />;
}

Now, the loading spinner is shown during the session check (isLoading is true) and during the redirect process (session is true). As a result, the loading spinner stays on the screen until the session check completes and the redirect operation is finalized. This effectively prevents the login page from briefly flashing on the screen before the redirect.

Upvotes: 0

Related Questions