realm / realm-js

Realm is a mobile database: an alternative to SQLite & key-value stores
https://realm.io
Apache License 2.0
5.81k stars 578 forks source link

MongoDB Realm User Object Lost with Every New App Version #5238

Open jasontulloch opened 1 year ago

jasontulloch commented 1 year ago

How frequently does the bug occur?

Sometimes

Description

We believe that when we deploy new versions of our production application it appears that the realm user object / file is deleted on random occasions. Users remain in our mobile application and run into several issues (because they no longer exist but are still logged in). All of our serverless functions stop working (user.functions.functionName).

We have ~resolved~ this by logging users out if the user and user.id objects do not exist but it is leading to a very poor user experience.

This happens on occasion which makes it extremely difficult to test. We have been unable to replicate this issue but are contacted by users several times a week when the issue occurs.

Stacktrace & log output

No response

Can you reproduce the bug?

No

Reproduction Steps

No response

Version

10.12.0

What services are you using?

Atlas App Services: Functions or GraphQL or DataAPI etc

Are you using encryption?

No

Platform OS and version(s)

IOS 15.0, iPhone 13

Build environment

Which debugger for React Native: ..

Cocoapods version

1.11.3

takameyer commented 1 year ago

@jasontulloch Thanks for reporting this. Can you provide more details on how you are handling your user sessions? What authorization method are you using? Any code samples would also be great. Also can you review your logs in the App Services dashboard to see if there are any error messages in there that could clue us into what is happening? You can also email me your App-ID and I can review it with the team responsible for the cloud.

jasontulloch commented 1 year ago

All of our user sessions are presently through email and password authentication.

The following code represents how we are signing in users (and mirrors what is discussed by MongoDB University ...

  const signIn = async (email, password) => {
        const creds = Realm.Credentials.emailPassword(email, password)
        const newUser = await app.logIn(creds)
        setUser(newUser)

        // We are using the code below to just control our navigation stacks
        if (newUser) {
            const profile = await newUser.functions.getMyProfile(newUser.id)
            setMyProfile(profile)
            if (profile.firstName == ''  || profile.newProfile) {
                signUpDetailsNew()
            } else {
                signUpDetailsExists()
            }
            AnalyticsService.signIn(newUser.id, {})
        }

    }

    return (
        <AuthContext.Provider value={{ 
            user, 
        }}>
            {children}
        </AuthContext.Provider>
    )

We have reviewed our logs when the issues have occurred and there are no errors. The User Id does not exist and it appears that our users are not even sending requests to MongoDB.

I will send our App Id with notes on when this issue has occurred this week as well.

Thanks!

takameyer commented 1 year ago

@jasontulloch Signup seems to be working, right? How are you persisting the user session on opening the application? Are you using app.currentUser?

takameyer commented 1 year ago

On another note, the template you referenced is quite old. We have since released a library called @realm/react that handles most of this logic for you. You can see an example here.

jasontulloch commented 1 year ago

Correct, signup is working without an issue. We are using app.user in our application, through the provider documented above. This is how we are fetching the user object throughout our app.

// useAuth is created in our provider.js file that has our authentication methods, like signIn mentioned above.
const useAuth = () => {
    const auth = useContext(AuthContext)
    if (auth == null) {
        throw new Error("useAuth() called outside of a AuthProvider?")
    }
    return auth
}

// Used on screens throughout our application to access the user object
const { user } = useAuth()
jasontulloch commented 1 year ago

We will look at the new example, thanks for sharing.

takameyer commented 1 year ago

Also, does this happen for Android users as well, or just iOS?

jasontulloch commented 1 year ago

It's on both android and ios.

UserIds from my separate email: '...757': ios '...63b': android '...d19': ios

takameyer commented 1 year ago

@jasontulloch One point, user sessions will not be able to refresh if they haven't been active for more than 3 months. Is it possible that the users have not accessed the app for a longer amount of time?

jasontulloch commented 1 year ago

Good to know in general... all the users who have reported bugs are active, certainly using our app within the same week, likely even the day before.

nirinchev commented 1 year ago

The 3 months timeframe starts from when they first login, not when they were last active.

jasontulloch commented 1 year ago

I’ll take a look. @takameyer can you confirm this is true as well? Just want to make sure as the last two comments are conflicting.

In the event that this is the case, is there a suggested approach on how to handle this? The user object simply expiring can’t be the best solution. Or better, is there any way to override this behavior? As far as I’m aware several successful apps do not end the user session after 3 months as it leads to poor user experience and lower retention.

Thanks so much for the help!

takameyer commented 1 year ago

@jasontulloch Yes, what @nirinchev has stated is correct. I'll confirm internally what may be the best approach. Perhaps it may be good to setup a time to look over your implementation and see if there are some improvements to be made.

jasontulloch commented 1 year ago

Thank you!

Unfortunately, we did some digging and one of the users mentioned has only had an account for 2 months, it is appears that this issue is not related to the user session timing out after 3 months. The bug seems to be related to something else.

Otherwise glad to make improvements that are warranted and necessary and definitely appreciated the guidance so far!

ianpward commented 1 year ago

hey @jasontulloch Product for Realm here - to be clear we have two different tokens: a refresh token - which expires after 60 days and an access token which expires after 30 minutes. The Realm SDK automatically refreshes the access token under the hood as long as the refresh token is still valid - no developer intervention is necessary.

For the refresh token, I would recommend always making a call on app startup to check to see if currentUser() is still valid - if not (which would indicate the user is expired) - you should push them to re-authenticate with their provider to obtain a new valid auth token.

I hope this helps - feel free to drop me a line at ian.ward@mongodb.com and we can discuss live for further clarification

jasontulloch commented 1 year ago

Thanks for joining the conversation @ianpward. Okay, this is a third, new answer so just want to confirm again... the user session token (when a user is logged in) lasts 60 days, not 90 days, and resets to a new 60 days when Realm.Credentials.emailPassword(email, password) is called?

Understood regarding the 30 minute access token, we are not experiencing issues on that end and it makes sense.

What's the 'best' way to confirm that the currentUser() is still valid? Presently, our solution is to check if the current user object and the currentUser.id exist, if it does, we execute our logic, if it does not we log out the user.

Our issue with this approach is that we continuously have users asking why they are being logged out to begin with and why they need to re-authenticate.

I will reach out separately as well, thank you for taking the time.