Logout after period of Inactivity Convex + Clerk - Expo APP
Still experiencing an issue with the user getting logged out. I've put all auth logic in a wrapper hook that I have around each auth route, as putting it in the root layout causes it to attempt navigation before routing is ready, which throws an error. Any help or if someone has an example of how to implement auth flow with onboarding check with convex & clerk.
40 Replies
@erquhart - Have added new thread
Where in this logic is the user getting unexpectedly logged out
I'm trying to figure this out—I think the issue stems from throwing an error in the Convex backend when the user isn't authenticated. It seems like queries are being executed before the user is authenticated, and since all backend functions are wrapped with an auth check, a large number of errors during initialization might be triggering a logout. If anyone has an example flow or proposed solution with onboarding logic id love to hear it ❤️
I'd definitely recommend using logs to track down what's happening leading up to the user getting logged out, helps clarify things
I'm a neanderthal when it comes to this, but it works - I'd pepper
console.log(0)
, console.log(1)
, etc. throughout this component, then check the logs. You can follow the logic, rerenders, figure out things that are running that you thought were being skipped, etc.We've been experiencing huge issues with exactly this as well. We think, however, that the issue may be Expo SecureStore. We found through Sentry monitoring that a
User interaction is not allowed
error was being thrown often by the Apple Keychain (which is what Expo SecureStore etc. use on iOS to store passwords etc. securely on the iOS device). This appeared to be after a longer period (>few hours) of the app being in the background, and the user then changing e.g. charging state or orientation, which somehow triggered Clerk or Expo to try to re-fetch authentication state while the app was in the background, which was disallowed due to Keychain security constraints. This eventually resulted in a "Error: You are signed out" from Clerk, which we assume then caused users to lose their local authentication state.
We also experience quite a lot of Convex errors stating that our users are not authenticated (we also use auth'ed wrappers), but think the error above is the original culprit.
Do you have Sentry set up? Otherwise maybe try to do that and see if it helps - literally takes less than an hour. We ended up moving away from Expo SecureStore, as there were several open GitHub issues mentioning this exact same issue, but no Expo fix seems to have come yet. We now use https://www.npmjs.com/package/react-native-keychain, and the error has completely disappeared from Sentry, so we are waiting and hoping to hear less and less about this issue from our users 🤣 as it's pretty much impossible to replicate on our side..
@ZachooZachoo's issue here is a chronic logout, like the user can't even get logged in and use the app without getting logged out. I don't think it's the same.
For what you're describing @djbalin, I've been troubleshooting web and Expo/RN logout/unauth issues a lot - especially the ones related to backgrounded apps/tabs - over the past few months. The native app I test with uses Sentry as well. I have never seen an issue that could be traced back to SecureStore, so I suspect what you're seeing may be Clerk specific. There is potential for logout after coming back from the background due to a remaining race condition or two with the Convex client, I have a fix for it that I've been running successfully for like a month, but I hate how the fix is implemented. Need to find a palatable way to introduce it. But when that's out it should take care of logout issues not specifically stemming from Clerk.
My test app also doesn't request permission to run in the background, so perhaps the SecureStore error isn't Clerk specific but only surfaces for apps that run in the background. I'll have to try that.
Lol just checked Sentry for something unrelated and that
User interaction is not allowed
from getValueWithKeyAsync
has been happening regularly for months. It's not actually causing any noticeable issues, logouts, or anything in the app, though. So I suspect it's the client race conditions that are actually causing your logouts, and it just all coincides with coming out of long background periods.Haha yea exactly that trace has been our only clue @erquhart! I found a range of GitHub issues describing the same issue so we investigated this. Does that trace not lead to
"Error: You are signed out"
for you? The two events are separated by ~30 other events (http stuff) occuring between them, but it's a common pattern. See my screenshot
That's what seemed like the smoking gun for us - this "you are signed out" error comes from Clerk as far as we can tell. We rationalized that the background getValueWithKeyAsync
fails due to Apple Keychain stuff (also indicated in the trace), and that therefore our Clerk provider cannot confirm authentication, and the user gets logged out. Interestingly, now that we changed our implementation of the tokenCache
passed to the ConvexClerk provider to just use react-native-keychain
(and not Expo SecureStore), these errors have completely vanished from our Sentry logs.
Have you been able to reproduce this users-getting-logged-out problem reliably on your end? We haven't, and given the nature of the delay necessary, it's been a headache to try to troubleshoot this.
Ah interesting I just looked into background permissions that you mentioned - I wasn't aware of explicitly having set any, but I can see that our
expo-video
actually allows background playback of videos. Also noticed in Sentry that virtually all of the You are signed out
error traces begin by the user playing a video and then, presumably, locking their device while still on that video. So expo-video
keeps the app alive in the background, but then maybe our Expo SecureStore settings did not allow for this background check
https://discord.com/channels/695411232856997968/1265984246309327001/1265984248217866322
This thread in the Expo discord also mentions this problem, where a solution was seemingly to relax the Apple Keychain access rules. We also implemented this in our switch from Expo SecureStore to react-native-keychain, and it's very likely that this has been what solved it for usAh okay, this is good info thanks for following up with details
Just seen this sorry have been away huge thanks will check it out and update here
So you are logged in and then after a period of inactivity sometimes several days or hours are logged out sounds similar to me but the logout is so unpredictable I will address sentry aswell
Let's keep each other updated on our findings about this pernicious thing :)) @Zachoo @erquhart
I'm experiencing an authentication/routing issue with my React/Expo app using Convex. The primary issue is that users are getting logged out after leaving the app in the background for extended periods (e.g., 3 days).
Steps to Reproduce
Open the app while signed in
Leave app in background for several days (or simulate by disabling WiFi, clearing from background, and reopening)
Return to app
Observed Behavior
App shows loading/splash screen for ~30 seconds
Incorrectly redirects to sign-in route
Console error:
if i then re-enable wifi
If I try to sign in, it says 'session already exists' -> returned from clerk
If I clear app from background and reopen with WiFi on, it works fine, user is logged in perfectly fine
My Guess
I believe the issue is in the auth flow and network handling:
When the app opens after being in background for days, the auth session with Convex has timed out
My auth guard (useAuthNavigation hook) checks authentication status
The Convex auth check fails or times out
The guard redirects to sign-in route as it thinks the user is unauthenticated
But the local Clerk session is still valid, causing the "session already exists" error
Relevant Code
How should I modify my auth navigation logic (if needed) to handle these background timeout scenarios and prevent false redirects to the sign-in screen when there's a session mismatch between Clerk and Convex? @erquhart @djbalin
or could it be bause i throw the error from convex ?
@Pedro Martinez chat moved here
could it be here in convex provider with clerk
" function useAuthFromClerk() {
const { isLoaded, isSignedIn, getToken, orgId, orgRole } = useAuth();
const fetchAccessToken = useCallback(
async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => {
try {
return getToken({
template: "convex",
skipCache: forceRefreshToken,
});
} catch {
return null;
}
},
// Build a new fetchAccessToken to trigger setAuth() whenever these change.
// Anything else from the JWT Clerk wants to be reactive goes here too.
// Clerk's Expo useAuth hook is not memoized so we don't include getToken.
// eslint-disable-next-line react-hooks/exhaustive-deps
[orgId, orgRole],
);"
when returning from background from a period of inactivity we may need a refresh ?
I would consider disabling wifi and extended background to be different cases. I honestly haven't looked much at what happens when you turn off data. And if you leave an app in the background for days in iOS without opening it, it's pretty much guaranteed to get shut down to reclaim memory. When you open an app that's been in the background that long, it's the same as opening it cold.
Some of what you described sounds very specific to disabling wifi. Can you describe just what happens in the extended background scenario? Is it the same?
that is just the best case i have found to replicate exact issue users are still reporting where they are logged in fine they use app close it come back a couple days later and it shows plash screen and then straight to /sign-in route when they login it says session allready exisits which makes sense as clerk should keep for 2 years as per my config. user cannont or is not routed away from this /signin route until they clear app from app draw half swipe up from bottom and clear app. upon re opening they are logged in fine again without ever needing to reenter password
is quite an infuriating but as it leads to negative reviews for our software with no clear understanding of whats happening
no clear sentryb logs eithier just seems there is a mismatch between the state of clerk and the state of convex upon re opening but i would assume this is handle by convex clerk provider
I just have not seen a condition like this that occurs when the app is closed out and reopens. If you see a splash screen, everything is starting from scratch. If there's a token in storage, Convex will still force refresh it initially, no matter what. I obviously can't say for certain, but this really sounds like a routing logic bug.
Have you been able to reproduce this in your own use of the app organically?
no i havent been aple to replicate but have seen it happen to a user in front of me
i shared routing logic above does it look okay ?
Hard to say, other things may end up influencing it beyond just that hook. Within the hook, though, things are hard to reason about due to the waterfall of variable reassignment. If I may, I suggest returning early for each route to make things more clear.
is there any example code docs about for it with an onboarding flow ?
It just needs to be more clear. If you return for each case you get a lot of clarity. Eg.,:
You're troubleshooting users being on the sign in page when they don't belong there. This way the other conditions after authentication state don't have an opportunity to overwrite the target route, so it's easier to reason about.
This is just an example, I didn't pay enough attention to your logic to make sure nothing was depending on the other approach. But hopefully this demonstrates the value of returning early and treating variables immutably wherever possible.
Have made more readable and still have same error root layout
if for any reason the user is redirected to sign in they are not redirected from there unless the app is cleared.
@Zachoo i had that issue as well, didn't get redirect from the AppLayout after the user was logged in, so in my SignInScreen added a useEffect listening to isAuthenticated from useConvexAuth hook. Something like this:
export default function SignInScreen() {
const router = useRouter();
const { isAuthenticated, isLoading: isAuthenticating } = useConvexAuth();
useEffect(() => {
if (isAuthenticated) {
router.push('/');
}
}, [isAuthenticated]);
.....
hmm i have the same logic but in my _layout file for sign in route did you have same in my mind the logic is the same ???
@Pedro Martinez as sign in screen will be wrapped by auth layout i also am redirected every time fine except for if app is in background for several days and then reopened the user is forced to clear app from background to re-sync with convex
is this issue you had and did use effect code resolve ??
added use effect still have same issue with app returning from background after an extended period of inactivity.
When you say return from background, it's also happening if in iOS the user swipes up to close the app and then opens it, right?
Yes as long as the app has not been cleared from background and has been sitting there for an extended period @erquhart
If they clear it from the background and then open it, everything works?
Yes correct @erquhart
I have shared a video of exactly what user sees sometimes if they leave app in background for a while and then reopen in pm
If a user opens the app and then leaves it in the background for a day or two—timing which seems random and possibly related to iOS unloading apps to save memory (not a full clear)—then reopens it, they are shown the sign-in screen. However, the logs show that Clerk is still authenticated (it fetches the token from storage), but Convex appears to treat the user as unauthenticated.
Happy to temporarily add you to GitHub so you can see structure ?
@Zachoo I had the same issue with users being logged out after returning from the background (at random intervals), and I made two changes that have been working so far:
* Switched from expo-secure-store to react-native-keychain.
* In my _layout, I was originally using Clerk for auth with const { isSignedIn, isLoaded } = useAuth();. I then switched to using useConvexAuth, but that introduced a new issue: after logging in, users couldn't navigate to the app's home screen. To fix that, I added a redirect on the sign-in screen, and that made it work.
I noticed your _layout is slightly different from mine. Here's what I currently have:
export default function DefaultLayout() { const { isAuthenticated, isLoading } = useConvexAuth(); // Get the tint color for the current color scheme const tintColor = Colors.primary; if (isLoading) { return <Loader color={Colors.border} />; } if (!isAuthenticated) { return <Redirect href="/sign-in" />; } return ( <Stack screenOptions={{ headerTitle: '', headerBackVisible: false, headerShadowVisible: false, headerTintColor: tintColor, }} > <Stack.Screen name="(tabs)" options={{ headerShown: false }} /> <Stack.Screen name="request" options={{ headerShown: false }} /> </Stack> ); }
Hmm maybe different issue I have no problem navigating user from sign in screen it is just that when app has been in background for say 3 days and then reopened user is show /sign in even tho there is an active clerk session only way to get back in is to entirely clearing app from background and reopening user is redirect and signed in fine
Can you add logs from your sign in page/component to print the authenticated state provided by both Convex and Clerk
Sure but I don’t know how to replicate to get console logs because it happens randomly can only get into the same state by disabling wifi
@erquhart nothing shows in sentry but i am on the home sign in route and when i click sign in it says session exists ?
Assuming you can repro this locally, have your component render the authenticated state info from Convex and Clerk. It's going to take this sort of digging to understand what's happening - testing assumptions, printing things on the screen (since you don't have a browser console to log to). Determining what the actual state is when the authenticated user is on the sign in screen is a good start.
The yeah I can show the sentry logs however they don’t trigger on the home screen or n this case but I am still not redirected so it is difficult to say what issue is. I have exact implementation as docs and sentry should trigger if there is a state mismatch which has been tested however the n this case it does not enter that error trigger but I am still not authentic which makes no sense as else I would be redirected. This is the same logic that executes when you clear app from background. The issue only appears after booting with wifi disabled waiting 30s then enabling wifi else leaving app in background for say 48h
Right, don't use Sentry for what I'm talking about here. Instead, in your sign in component or page component, whichever component has authentication state for Convex and Clerk, render those state values right in your component locally so you can view them on your iOS device when you're reproducing this bug. then you can look directly at the state. Does that make sense?
But also, you can use console.log and look at your expo cli to output them for you, or in the debugger console (hit "j" while the expo cli is running)
This is strange session exists but clerk use auth hook returns false

@erquhart this is what im shown but if i clear app from multi tasking UI and login everything is fine ...
What are user id and session id when the bug happens
(I mean defined or undefined)
undefined

I see you have verbose logging on, can you share the cli logs that are being output when you reproduce this issue
You can also add a telemetry prop to
ClerkProvider
that may provide some additional insight in the logs telemetry={{ debug: true, disabled: false }}
I want to keep the troubleshooting conversation here so it benefits others, but if you want to share logs via dm that's fine