I am currently developing an app for work with react native, expo and supabase. The problem i am facing is the following: ERROR [AuthApiError: Invalid Refresh Token: Refresh Token Not Found]
Which happens when my session is destroyed, the error itself isnt the problem for me the problem is that my app displays a white screen when getting this error. And i havent found out why it does that.
This is a piece of code related to the error:
import { supabase } from "@/lib/supabase";
import { AuthApiError, Session } from "@supabase/supabase-js";
import { router } from "expo-router";
import { PropsWithChildren, createContext, useContext, useEffect, useState } from "react";
type AuthData = {
session: Session | null;
profile: any;
loading: boolean;
isAdmin: boolean;
};
const AuthContext = createContext<AuthData>({
session: null,
profile: null,
loading: true,
isAdmin: false,
});
export default function AuthProvider({ children }: PropsWithChildren) {
const [newSession, setNewSession] = useState<Session | null>(null);
const [profile, setProfile] = useState<any>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
setNewSession(session);
if (session) {
// Fetch profile when a session exists
supabase
.from('profiles')
.select('*')
.eq('id', session.user.id)
.single()
.then(({ data }) => {
setProfile(data || null);
setLoading(false);
})
} else {
console.log('No session found');
setLoading(false);
router.push('/(auth)/sign-in');
}
});
supabase.auth.onAuthStateChange(async (_event, session) => {
setNewSession(session);
if (session) {
// fetch profile
const { data } = await supabase
.from('profiles')
.select('*')
.eq('id', session.user.id)
.single();
setProfile(data || null);
}
});
}, []);
return <AuthContext.Provider value={{session: newSession, loading, profile, isAdmin: profile?.role === 'ADMIN'}}>{children}</AuthContext.Provider>;
}
export const useAuth = () => useContext(AuthContext);
The steps i took to replicate my error:
If i do this the other way arround i get a white screen on my phone.
I have tried to use a try catch, and to check if there is no session. And if so send the user back to the sign in page. But that didnt prevent my app from crashing.
I am tempted to think that the crash occurs when an error occurs.
I realy hope anyone has some tips for me.
EDIT:
EDIT 2: @TommyBs gave me a tip that may would have worked, but i might have done it the wrong way?
Here is the code i made after @TommyBs his tip:
useEffect(() => {
// Set up the listener for auth state changes
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
if (event === 'SIGNED_OUT') {
// Handle sign out by setting session to null
setNewSession(null);
router.push('/(auth)/sign-in');
setLoading(false);
} else if (session) {
// Update the session state if a session is present
setNewSession(session);
setLoading(false);
} else {
// This handles the case when there is no session (e.g., the user is not logged in)
setNewSession(null);
router.push('/(auth)/sign-in');
setLoading(false);
}
}
);
// Clean up the subscription on component unmount
return () => {
subscription.unsubscribe();
};
}, [router]);
useEffect(() => {
if (newSession) {
console.log('session found');
const fetchProfile = async () => {
const { data } = await supabase
.from('profiles')
.select('*')
.eq('id', newSession.user.id)
.single();
setProfile(data || null);
}
fetchProfile(); // Call the async function
} else {
console.log(' No session found');
}
}, [newSession]);
Edit 3:
export default function AuthProvider({ children }: PropsWithChildren) {
const [newSession, setNewSession] = useState<Session | null>(null);
const [profile, setProfile] = useState<any>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Set up the listener for auth state changes
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
if (event === 'SIGNED_OUT') {
// Handle sign out by setting session to null
setNewSession(null);
//router.push('/(auth)/sign-in');
setLoading(false);
} else if (session) {
// Update the session state if a session is present
setNewSession(session);
setLoading(false);
console.log(session);
// fetch profile
console.log('session found');
setTimeout(async () => {
const { data } = await supabase
.from('profiles')
.select('*')
.eq('id', session.user.id)
.single();
setProfile(data || null);
}, 0);
} else {
// This handles the case when there is no session (e.g., the user is not logged in)
setNewSession(null);
//router.push('/(auth)/sign-in');
setLoading(false);
}
}
);
// Clean up the subscription on component unmount
return () => {
subscription.unsubscribe();
};
}, []);
return <AuthContext.Provider value={{session: newSession, loading, profile, isAdmin: profile?.role === 'ADMIN'}}>{children}</AuthContext.Provider>; }
export const useAuth = () => useContext(AuthContext);
The whole issue luckily wasnt because of the error invalid refresh token. It was part of the issue, but it wasnt the direct cause of the issue.
The issue had to do with the following piece of code:
const [loaded] = useFonts({
SpaceMono: require('../../assets/fonts/SpaceMono-Regular.ttf'),
});
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
If the font didnt load the page never loads and it just shows a white screen. What fixed my problem was to look up the official way to do this. So i found the following in the docs of expo. https://docs.expo.dev/versions/latest/sdk/splash-screen/
I followed this and used the recommended way of loading the fonts, and hiding the splashscreen. And it Fixed the whole problem for me.
For any questions or needed information just comment and ill explain happily.