Convex Auth signaling unauthenticated in RN after inactive
I haven't been able to capture much on this, but I've seen this consistently since migrating to Convex Auth:
- After opening the iOS app when it's been in the background, we're redirecting to sign in
- The logic currently in place will only do this if both
isAuthenticated
and isLoading
from useConvexAuth()
are false
.
- I've added logic to listen for further changes to these values, and also added a user query that waits for an authenticated user to come back, none of this is working
- Closing the app completely and reopening causes the currently authenticated user to be recognized
I'm trying to get more insight to share, but wanted to bring this here in case anything jumps out at anyone. It's becoming a bit of a pain that the app effectively redirects to sign on most occasions, even though the user is authenticated. I'm not building for web, and am only assuming this is RN specific. Maybe something to do with secure storage?41 Replies
How are you doing the redirecting? Can you share your code?
It's a layout file that's close to root, wraps the app, and is wrapped by the provider tree.
My naive take is
isLoading
should always be true if auth state hasn't been ascertained. I don't know how it's flipping to unauthenticated when the auth state hasn't changed.
Somehow it's possible for isLoading
and isAuthenticated
to both be false while the user is still authenticated.Sounds like a bug
Do you have a repo I can repro it with?
I need to put one together, I'll do that
@erquhart please upgrade convex and @convex-dev/auth to latest
Done. Find something possibly related to this?
Possibly, yeah
See if you can still reproduce your issue
Still happening it seems. I'll let you know when I have a repro case. It's a pain because it requires a running app on iOS. Might work in simulator, but still a pain to repro in a shareable fashion. Will be easier once I'm on test flight.
I'd try logging from secure storage to make sure it's working as expected
@Michal Srb I have logging in the secure storage object now, just caught it in action:
As far as I can tell, when I opened the app (which was in the background for some time),
removeItem
ran immediately. Then getItem
ran, and of course, found no token. Then you can see logged updates there from useConvexAuth()
, at which point I was redirected to sign in.
Did not sign in, closed the app and reopened, here are the logs that followed (duplicates removed):
So ended up authenticated without having to sign in again.
Naive observations:
- the initial removeItem
on the refresh token seems unexpected
- after getItem
for refresh token failed, __convexAuthJWT_<>
wasn't accessed before isAuthenticated
was set to false. I would have expected an attempt to get a new token before marking the user unauthenticated
appState
is using react native's AppState.current
, which I don't believe triggers rerenders. I've updated to using an event listener, so should capture any changes in app state that might be affecting things moving forward, will update if so.@erquhart I believe I'm encountering the same issue with React Native, and it's becoming a significant problem for us. In our case, we're using Firebase Auth instead of Convex Auth.
Scenario:
Users are logged in, and after some time, when the app returns from the background, the Convex auth hook returns isLoading: false, isAuthenticated: false, even though they were authenticated before the app went into the background. When logging these requests on the backend, the userIdentity?.subject is null.
I suspect this might be related to a race condition between auth tokens expiring and not being refreshed in time while trying to reconnect to Convex. What leads me to this conclusion is that if I put the app in the background for 5 minutes, I don't encounter any issues. However, if I leave the app in the background for 2 days, I am guaranteed to face the issue, suggesting that the problem is related to the expiration of ID tokens. That could also be related that the iOS app state becomes "suspended" after some time. I haven't checked if the issue occurs on Android
Thanks for sharing this, interesting that it's happening without using Convex Auth. I narrowed it down to Convex Auth because I used Convex with Clerk for about a year and never encountered the issue. I'll need to take a look at my implementation from before and see if maybe something about the approach was somehow getting around the issue successfully.
cc/ @Michal Srb just wanted to make sure you saw all of the latest info here. Signs seem to be pointing in the general direction of token refresh (as far as one can tell).
@Riki does closing and reopening your app work when the bug occurs for you or does the user still have to reauthenticate?
Closing and reopening the app works for me, but that's because I use a different approach than you. Instead of using authentication flags/boolean from Convex hook, I use the the ones from Firebase, so basically we have something similar to this:
I am still having the same issue as you as, some of my queries require authentication, and if the user is not authenticated, I have decided to make them throw a ConvexError.
TLDR: after some time the user loses its auth status on Convex => queries are still running => they throw an error => it crashes the app => by closing and reopening the app, firebase auth still keeps the authentication and convex hook "reboots" with auth again.
@erquhart It seems we have the same symptoms, but I am not 100% sure yet that is because of the same causes
In case that helps, this is what we pass to the convex component:
I've had an ongoing parallel issue where a user signs in and is never redirected. I'm now seeing that when this occurs, the user is being successfully signed via convex auth, but
isAuthenticated
and isLoading
aren't changing, so there's no signal that the user is authenticated.@Michal Srb or @erquhart let me know if you prefer that I open another discussion, but I am pretty sure about what's happening right now on my side
Firebase tokens have an expiration time of one hour. When I close the app for 1hour+, and I open it again with a bad network on my phone, the connection with convex servers seems to run before the token is refreshed by Firebase. Given I throw an error on Convex side if the user is not authenticated, the app crashes (I handle it with error boundaries)
@Riki it does seem possible that yours is a separate issue. OP here has to do with a valid auth session not resulting in
isAuthenticated
being set to true. But I still don't understand the problem enough to be certain they're different.I'm verbose logging from convex auth now, and I see that a token is present and acknowledged, but isAuthenticated remains false. Again, closing and reopening the app fixes it.
There's a bug somewhere where convex auth client lib is not setting
isAuthenticated
in the main convex client.Ah yes indeed, it looks like it's a different issue
cc/ @Michal Srb just want to make sure my breadcrumbs here haven't been overlooked, left some info here as well: https://discord.com/channels/1019350475847499849/1272981519115223120/1274436848810332210
(FYI I do have this on my radar as something to look into, but haven't looked deeply yet)
Thanks for the update!
@sshader could you please look into this? I'm also experiencing this issue with my React Native app, and it's very frustrating for users to be unexpectedly logged out.
I'm using custom auth, connected to AWS Cognito. The setup follows the approach described here: https://docs.convex.dev/auth/advanced/custom-auth
It's been hard to capture any more than what I shared before in this thread, but my app generally appears logged out whenever opened from the background. Still working through launch and App Store review, but this will be top of list pretty soon.
I suspect it is related to
userIdentity
being set to null
under some conditions, when it should not be.
Starting to look into this -- I understand it's frustrating and thanks for the patience so far (for context, I'm still in the process of ramping up on Convex Auth and trying to field all the incoming questions in this area, so my turnaround time is slower than I'd like)
Definitely understand. Hope some of the info in the thread is helpful to narrow things down. Will add more if I find anything.
Any tips for reproducing? Such as how consistently this happens when coming back from backgrounded + how long the app is backgrounded? I currently have an Expo app that I'm opening on my iPhone with all the same logging. I've been able to hit the sign in screen when backgrounding my app for longer than the JWT duration (which I set to 1 min for testing, but defaults to 1 hour). Does any of this seem consistent with what y'all have been seeing?
I'm not totally sure on the timing, doesn't seem to take an hour for me but that is not based on any data. Could very well be an hour.
Thanks sshader. Could it be related to web socket’s connection expiring? Is there an idle timeout or anything like this when using web sockets? I’ll try observing how long the app needs to be in the background for before it triggers this issue.
Ok try upgrading Convex Auth (0.0.64). I'm hopeful that this is the fix (https://github.com/get-convex/convex-auth/pull/63)
GitHub
Add refresh token mutex + don't leak beforeunload listener by sshad...
I believe this fixes two bugs:
In React Native (or any environment where window.navigator.locks is undefined), we can get re-entrant calls to fetchAccessToken where one call clears the old refresh...
Upgrading now! The code change and assessment definitely feel spot on.
If I'm right, I think this means that for web, we cutely avoided this problem of callbacks in the same tab stepping on each other by fixing a different problem of keeping different tabs from stepping on each other.
I’m using custom auth though, not Convex Auth.
It could be a seperate issue, I guess
@adam -- if you could share more of your code or turn on verbose logs and share logs (
{ verbose: true }
when instantiating ConvexReactClient
), I'm happy to dig more. The Convex Auth issue was an unexpected interleaving of fetchAccessToken
calls resulting in one of them returning null
incorrectly. Curious what your fetchAccessToken
looks like and whether it could have a similar problem (also sorry -- missed that this was custom auth vs. a custom auth provider with convex auth)Still need to get a production release out to confirm whether this is fixed for me - I've seen it happen once since in dev, but that may have been a fluke. Will circle back asap.
Thanks so much for digging into this @sshader!
@sshader I've confirmed the issue I'm experiencing is related to the refresh token flow. Here are the details to reproduce:
1) Use Convex custom auth with AWS Cognito and Expo
2) Create a production build of the app
3) Set Cognito refresh token to min (60min)
4) Open the app and then send it to the background
5) Bring the app back to the foreground after 60min to observe the issue
const userIdentity: UserIdentity | null = await ctx.auth.getUserIdentity();
I assume it's incorrectly being set to null
here.
I'm working around this issue for now by extending my refresh token expiry period.
For reference here is my fetchAccessToken
function:
@sshader I'm still a bit away from my next production release, but I'm pretty confident this is fixed. Used to happen many times a day in dev and has not happened since this fix. Marking resolved.
@adam I know both of our issues seem to overlap around token refresh, but my issue and the fix were Convex Auth specific - it didn't happen when we were on clerk. I'd recommend opening a separate post focused on what you're experiencing with custom auth. Your issue may be the same as @Riki's based on his comments, but it doesn't look like he made a separate post either.
Thanks for the added info -- what is
Auth
here? (my guess is https://www.npmjs.com/package/amazon-cognito-auth-js but I want to check)npm
amazon-cognito-auth-js
Amazon Cognito Auth JavaScript SDK. Latest version: 1.3.3, last published: 4 years ago. Start using amazon-cognito-auth-js in your project by running
npm i amazon-cognito-auth-js
. There are 25 other projects in the npm registry using amazon-cognito-auth-js.import { Amplify, Auth, Hub } from 'aws-amplify';
@erquhart is right, for a better history search and further comments, it may be better to not mix the topics. If you don't mind, let's discuss to this new topic dedicated to this JTW expiration issue: https://discord.com/channels/1019350475847499849/1283351896765235230