aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.43k stars 2.13k forks source link

Getting logged out in React Native #7944

Closed samih7 closed 2 years ago

samih7 commented 3 years ago

Describe the bug We use amplify-js 3.3.20 in our React Native app and some of our users are getting logged out from once very couple of days to several times a day.

We retrieve user's token with the currentSession API to then redirect the user accordingly (to login page or home page), and it seems that this function sometimes throws a 'No current user' error even if the user was previously logged in. Before that, we were using currentAuthenticatedUser instead and the same problem was happening ('The user is not authenticated' error was thrown).

We provide a custom storage using react-native-sensitive-info to store user data securely (we created a wrapper based on https://docs.amplify.aws/lib/auth/manageusers/q/platform/js#managing-security-tokens). To isolate the problem, we also released a version without react-native-sensitive-info where we simply passed in an AsyncStorage implementation as in https://docs.amplify.aws/lib/auth/manageusers/q/platform/js#managing-security-tokens but we still had the logout issue, which suggests it does not come from this other library. We also checked that ID & access tokens were correctly refreshed by aws-amplify which seems to be the case.

Resembles https://github.com/aws-amplify/amplify-js/issues/4351.

To Reproduce

  1. Federated login or via username/password
  2. Stay logged in for a while, close the app, reopen it
  3. At some point, logout occurs (currentSession throws an error)

Expected behavior

Stay logged in if we were previously logged in before.

What is Configured? If applicable, please provide what is configured for Amplify CLI:

Environment ``` System: OS: macOS 11.2.3 CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz Memory: 402.45 MB / 32.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 12.18.4 - /usr/local/bin/node npm: 6.14.11 - /usr/local/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman Browsers: Chrome: 89.0.4389.82 Safari: 14.0.3 npmPackages: @apollo/client: 3.3.7 => 3.3.7 @babel/core: 7.12.10 => 7.12.10 @babel/runtime: 7.12.5 => 7.12.5 @graphql-codegen/cli: 1.20.1 => 1.20.1 @graphql-codegen/fragment-matcher: 2.0.1 => 2.0.1 @graphql-codegen/import-types-preset: 1.18.1 => 1.18.1 @graphql-codegen/near-operation-file-preset: 1.17.13 => 1.17.13 @graphql-codegen/typescript: 1.20.2 => 1.20.2 @graphql-codegen/typescript-operations: 1.17.14 => 1.17.14 @graphql-codegen/typescript-react-apollo: 2.2.1 => 2.2.1 @react-native-async-storage/async-storage: 1.13.3 => 1.13.3 @react-native-community/masked-view: 0.1.10 => 0.1.10 @react-native-community/netinfo: 5.9.10 => 5.9.10 @react-native-community/slider: 3.0.3 => 3.0.3 @react-native-firebase/analytics: 10.1.1 => 10.1.1 @react-native-firebase/app: 10.1.0 => 10.1.0 @react-native-firebase/messaging: 10.1.1 => 10.1.1 @react-native-firebase/perf: 10.1.1 => 10.1.1 @react-native-picker/picker: 1.9.10 => 1.9.10 @react-navigation/bottom-tabs: 5.11.7 => 5.11.7 @react-navigation/material-top-tabs: 5.3.13 => 5.3.13 @react-navigation/native: 5.9.2 => 5.9.2 @react-navigation/stack: 5.14.2 => 5.14.2 @sentry/react: 5.29.2 => 5.29.2 @sentry/react-native: 2.1.1 => 2.1.1 @storybook/addon-actions: 5.3.21 => 5.3.21 @storybook/addon-knobs: 5.3.21 => 5.3.21 @storybook/addon-links: 5.3.21 => 5.3.21 @storybook/addon-ondevice-actions: 5.3.23 => 5.3.23 @storybook/addon-ondevice-knobs: 5.3.23 => 5.3.23 @storybook/addon-storyshots: 6.0.27 => 6.0.27 @storybook/addons: 6.0.27 => 6.0.27 @storybook/react-native: 5.3.23 => 5.3.23 @storybook/react-native-server: 5.3.23 => 5.3.23 @testing-library/jest-native: 4.0.1 => 4.0.1 @testing-library/react-hooks: 3.4.2 => 3.4.2 @testing-library/react-native: 7.1.0 => 7.1.0 @types/i18n-js: 3.8.0 => 3.8.0 @types/jest: 26.0.20 => 26.0.20 @types/lodash: 4.14.168 => 4.14.168 @types/react: 16.9.52 => 16.9.52 @types/react-native: 0.63.45 => 0.63.45 @types/react-native-permissions: 2.0.0 => 2.0.0 @types/react-native-share: 1.1.4 => 1.1.4 @types/react-native-vector-icons: 6.4.6 => 6.4.6 @types/react-redux: 7.1.16 => 7.1.16 @types/react-test-renderer: 16.9.3 => 16.9.3 @types/recompose: 0.30.7 => 0.30.7 @types/redux-mock-store: 1.0.2 => 1.0.2 @types/styled-components-react-native: 5.1.1 => 5.1.1 @types/uuid: 3.4.6 => 3.4.6 @types/yup: 0.29.7 => 0.29.7 @typescript-eslint/eslint-plugin: 4.16.1 => 4.16.1 @typescript-eslint/parser: 4.16.1 => 4.16.1 @welldone-software/why-did-you-render: 5.0.0 => 5.0.0 amazon-cognito-identity-js: 4.5.5 => 4.5.5 apollo-link-rest: 0.8.0-beta.0 => 0.8.0-beta.0 apollo-link-timeout: 4.0.0 => 4.0.0 aws-amplify: 3.3.20 => 3.3.20 babel-jest: 26.6.3 => 26.6.3 babel-plugin-inline-import: 3.0.0 => 3.0.0 babel-plugin-transform-react-remove-prop-types: 0.4.24 => 0.4.24 babel-plugin-transform-remove-console: 6.9.4 => 6.9.4 core-js: 3.8.3 => 3.8.3 cross-fetch: 3.0.6 => 3.0.6 date-fns: 2.16.1 => 2.16.1 eslint: 7.21.0 => 7.21.0 eslint-config-prettier: 8.1.0 => 8.1.0 eslint-plugin-import: 2.22.1 => 2.22.1 eslint-plugin-prettier: 3.3.1 => 3.3.1 eslint-plugin-react: 7.22.0 => 7.22.0 eslint-plugin-react-hooks: 4.2.0 => 4.2.0 eslint-plugin-react-native: 3.10.0 => 3.10.0 eslint-plugin-react-native-a11y: 2.0.4 => 2.0.4 eslint-plugin-simple-import-sort: 7.0.0 => 7.0.0 eslint-watch: 7.0.0 => 7.0.0 expo-av: 9.0.0 => 9.0.0 expo-video-thumbnails: 4.3.0 => 4.3.0 filesize: 6.1.0 => 6.1.0 flipper-plugin-performance: 1.2.0 => 1.2.0 formik: 2.2.6 => 2.2.6 graphql: 15.5.0 => 15.5.0 graphql-anywhere: 4.2.7 => 4.2.7 husky: 4.3.8 => 4.3.8 i18n-js: 3.5.0 => 3.5.0 jest: 26.6.3 => 26.6.3 jest-styled-components: 7.0.3 => 7.0.3 jetifier: 1.6.6 => 1.6.6 lint-staged: 10.5.3 => 10.5.3 lodash: 4.17.20 => 4.17.20 metro-react-native-babel-preset: 0.64.0 => 0.64.0 mockdate: 3.0.2 => 3.0.2 patch-package: 6.4.4 => 6.4.4 polished: 4.0.5 => 4.0.5 prettier: 2.2.1 => 2.2.1 querystring: 0.2.0 => 0.2.0 react: 16.13.1 => 16.13.1 react-devtools: 3.6.3 => 3.6.3 react-dom: 16.13.1 => 16.13.1 react-error-boundary: 2.2.3 => 2.2.3 react-native: 0.63.4 => 0.63.4 react-native-config: git://github.com/luggit/react-native-config.git#89a602bf8be3808838403a97afaf915caeec76c2 => 0.11.7 react-native-date-picker: 3.2.9 => 3.2.9 react-native-dev-menu: 4.0.2 => 4.0.2 react-native-device-info: 5.3.0 => 5.3.0 react-native-email-link: 1.10.0 => 1.10.0 react-native-fast-image: 8.3.4 => 8.3.4 react-native-flipper: 0.79.0 => 0.79.0 react-native-flipper-apollo-devtools: 0.0.2 => 0.0.2 react-native-gesture-handler: 1.9.0 => 1.9.0 react-native-get-random-values: 1.5.1 => 1.5.1 react-native-haptic-feedback: 1.11.0 => 1.11.0 react-native-image-crop-picker: 0.36.0 => 0.36.0 react-native-inappbrowser-reborn: 3.5.1 => 3.5.1 react-native-keyboard-aware-scroll-view: 0.9.3 => 0.9.3 react-native-lightbox-v2: 0.8.7 => 0.8.7 react-native-linear-gradient: 2.5.6 => 2.5.6 react-native-localize: 2.0.1 => 2.0.1 react-native-modal: 11.6.1 => 11.6.1 react-native-parsed-text: 0.0.22 => 0.0.22 react-native-performance: 1.4.0 => 1.4.0 react-native-performance-flipper-reporter: 1.2.0 => 1.2.0 react-native-permissions: 2.0.3 => 2.0.3 react-native-picker-select: 8.0.4 => 8.0.4 react-native-reanimated: 1.13.2 => 2.0.0 react-native-safe-area-context: 2.0.3 => 2.0.3 react-native-screens: 2.17.1 => 2.17.1 react-native-sensitive-info: 5.5.8 => 5.5.8 react-native-share: 2.0.0 => 2.0.0 react-native-shimmer-placeholder: 2.0.6 => 2.0.6 react-native-splash-screen: 3.2.0 => 3.2.0 react-native-svg: 12.1.0 => 12.1.0 react-native-svg-transformer: 0.14.3 => 0.14.3 react-native-tab-view: 2.15.2 => 2.15.2 react-native-unimodules: 0.12.0 => 0.12.0 react-native-url-preview: 1.1.9 => 1.1.9 react-native-vector-icons: 6.6.0 => 6.6.0 react-redux: 7.2.2 => 7.2.2 react-test-renderer: 16.14.0 => 16.14.0 redux: 4.0.5 => 4.0.5 redux-flipper: 1.4.2 => 1.4.2 redux-mock-store: 1.5.4 => 1.5.4 redux-persist: 6.0.0 => 6.0.0 redux-saga: 1.1.3 => 1.1.3 reselect: 4.0.0 => 4.0.0 styled-components: 5.2.1 => 5.2.1 typesafe-actions: 5.1.0 => 5.1.0 typescript: 4.2.2 => 4.2.2 uuid: 3.3.3 => 3.3.3 yup: 0.29.3 => 0.29.3 npmGlobalPackages: apollo: 2.31.2 expo-cli: 4.3.0 npm: 6.14.11 parcel-bundler: 1.12.4 ```

Smartphone (please complete the following information):

therealemjy commented 3 years ago

We are experiencing the exact same issue, with a similar setup, except we store the token in the AsyncStorage using a wrapper like you did. Our users are also getting logged out after some time. I'm very interested to get more infos on that issue.

Let me know if I can assist with anything to get this progressing.

amhinson commented 3 years ago

Sorry you are experiencing issues! Would you be able to provide a larger code snippet or perhaps a basic sample repo that shows this behavior? I'd like to reproduce this, but I haven't seen this behavior before with a React Native app I have configured with federation.

I'm mainly interested in where/how currentSession is used as well as how exactly Amplify is configured.

Also, have you reproduced this behavior without passing in a custom storage solution? Amplify will use AsyncStorage by default, so there isn't a need to pass it in explicitly.

samih7 commented 3 years ago

@amhinson Does the following help?

Pretty sure we were able to reproduce it without passing any storage implementation yes, then we did pass one because of the following issue. Seems sessions problems can arise from aws-amplify still using the React Native AsyncStorage implementation while it's now been moved to https://github.com/react-native-async-storage/async-storage.

import Auth from '@aws-amplify/auth';
import Amplify, { Hub, HubCallback } from '@aws-amplify/core';
import React from 'react';
import { Linking } from 'react-native';
import Config from 'react-native-config';
import InAppBrowser from 'react-native-inappbrowser-reborn';

import { navigationRef } from './navigation/helpers';

Amplify.configure({
  aws_project_region: Config.AWS_REGION,
  aws_cognito_region: Config.AWS_REGION,
  aws_user_pools_id: Config.AWS_USER_POOLS_ID,
  aws_user_pools_web_client_id: Config.AWS_USER_POOLS_WEB_CLIENT_ID,
  // storage: SecureStorage,
  oauth: {
    domain: Config.OAUTH_DOMAIN,
    scope: ['email', 'profile', 'openid', 'aws.cognito.signin.user.admin'],
    redirectSignIn: Config.OAUTH_URI_SCHEME,
    redirectSignOut: Config.OAUTH_URI_SCHEME,
    responseType: Config.OAUTH_RESPONSE_TYPE,
    urlOpener: async (url: string, redirectUrl: string) => {
      if (url.includes('logout')) {
        return;
      }

      if (!(await InAppBrowser.isAvailable())) {
        await Linking.openURL(url);

        return;
      }

      const result = await InAppBrowser.openAuth(url, redirectUrl, {
        showTitle: false,
        enableUrlBarHiding: true,
        enableDefaultShare: false,
        ephemeralWebSession: false,
      });

      if (result.type === 'success') {
        await Linking.openURL(result.url);
      }
    },
  },
});

// Used before firing API calls
export const getAuthorizationToken = async (): Promise<string | null> => {
  const session = await Auth.currentSession();

  const token = session?.getIdToken()?.getJwtToken();

  return typeof token === 'string' ? `Bearer ${token}` : null;
};

const useAuthListener = (): void => {
  const handleAuth = React.useCallback<HubCallback>(
    async ({ payload: { event, data } }) => {
      switch (event) {
        case 'signIn':
          {
            const token = data?.signInUserSession?.idToken?.jwtToken;
            const groups =
              data?.signInUserSession?.accessToken?.payload['cognito:groups'];

            if (token && groups) {
              if (groups.includes('USER')) {
                navigationRef.current?.navigate('Home');
              } else if (groups.includes('BETA_CANDIDATE')) {
                navigationRef.current?.navigate('Beta');
              }
            }
          }
          break;
        case 'signOut':
          navigationRef.current?.navigate('Login');

          // Cleanup some internal storages
          break;
        default:
          break;
      }
    },
    []
  );

  React.useEffect(() => {
    Hub.listen('auth', handleAuth);

    return (): void => {
      Hub.remove('auth', handleAuth);
    };
  }, [handleAuth]);
};

const App: React.FC = () => {
  useAuthListener();

  React.useEffect(() => {
    async function redirectUser(): Promise<void> {
      try {
        const session = await Auth.currentSession();

        if (!session?.getIdToken()?.getJwtToken()) {
          throw new Error("Auth.currentSession hasn't returned any session");
        }

        navigationRef.current?.navigate('Home');
      } catch {
        navigationRef.current?.navigate('Login');
      }
    }

    redirectUser();
  }, []);

  // Bunch of screens triggering actions such as:
  // Auth.federatedSignIn, Auth.signIn, Auth.signOut, Auth.signUp, Auth.resendSignUp, Auth.forgotPassword, Auth.confirmSignUp
  return <></>;
};

export default App;
nerdygirl commented 3 years ago

I'm having this same issue. Here's how I reproduce in an emulator:

  1. Sign into the app, see my home screen
  2. Hit "r" in the terminal to reload the app and I'm booted to the login screen when calling Auth.currentSession() fails

I'm using a custom storage object as described in the Amplify docs. It turns out that the storage object isn't loading all the data it needs from memory before we try to validate the session. So the order of events is:

  1. User returns to app.
  2. Auth.currentSession() throws an error because there are no current credentials.
  3. The sync() method on the storage object completes, so now local memory is populated with remembered credentials.
  4. But it's too late! We already redirected to the login screen after step 2 because we thought the user wasn't authenticated.

I'm toying around with different solutions now. The most promising so far is to add a slight delay (100ms) before the first call to Auth.currentSession() to give the sync() method time to complete.

samih7 commented 3 years ago

@nerdygirl Thanks for the explanation. Just to make sure we're talking about the same issue: You mentioned that if you hit "r" in the terminal, you get redirected to login since currentSession() fails. What happens if you refresh again/restart the app after that? In my case, the logout is permanent: the storage has been cleared out and it doesn't matter if I try restarting the app several times, I just won't get logged in.

nerdygirl commented 3 years ago

Yeah, the same thing happens to me in that case.

And although I swear this was working on both Android and iOS previously, it now only appears to work on Android. The difference seems to be that the keys stored in AsyncStorage are now wiped out on iOS, even if I'm just refreshing the app.

Are you noticing any differences between Android and iOS, @samih7?

samih7 commented 3 years ago

@nerdygirl Both occurring on iOS and Android for me.

I just got the issue while debugging our app, I logged out the storage content and got the following: [{"key": "amplify-redirected-from-hosted-ui", "service": "my_service", "value": "true"}, {"key": "amplify-signin-with-hostedUI", "service": "my_service", "value": "true"}]

So it really looks like user-related data has been cleared out from the storage. @amhinson

Edit: once again, Auth.currentSession is failing with 'No current user' error

Goszu commented 3 years ago

Same issue. Using own implementation of storage based on SecureStore (expo-secure-store). No issues on Amplify auth v2, started happening on v3.

rcCaregiven commented 3 years ago

I have a new theory I'm testing out. Hoping it's helpful to someone (or you can also test and we can share notes).

I followed instructions from AWS Cognito docs to write my custom storage class. Here's the code they provide for setItem() and sync() with a couple of comments from me:

    static setItem(key, value) {
        AsyncStorage.setItem(MYSTORAGE_KEY_PREFIX + key, value);  // I REPLACED ASYNCSTORAGE WITH KEYCHAIN
        dataMemory[key] = value;
        return dataMemory[key];
    }

    static sync() {
        if (!MyStorage.syncPromise) {
            MyStorage.syncPromise =  new Promise((res, rej) => {
                AsyncStorage.getAllKeys((errKeys, keys) => {  // BUT MY KEYS AREN'T IN ASYNC STORAGE
                    if (errKeys) rej(errKeys);
                    const memoryKeys = keys.filter((key) => key.startsWith(MYSTORAGE_KEY_PREFIX));
                    AsyncStorage.multiGet(memoryKeys, (err, stores) => {
                        if (err) rej(err);
                        stores.map((result, index, store) => {
                            const key = store[index][0];
                            const value = store[index][1];
                            const memoryKey = key.replace(MYSTORAGE_KEY_PREFIX, '');
                            dataMemory[memoryKey] = value;
                        });
                        res();
                    });
                });
            });
        }
        return MyStorage.syncPromise;
    }

In my code, I replaced this line:

        AsyncStorage.setItem(MYSTORAGE_KEY_PREFIX + key, value);

with a call to Keychain.setInternetCredentials()

One million tests and console.log statements later, I believe that what happens is this:

  1. User logs in
  2. Cognito calls setItem() to store 7 key/value pairs
  3. User leaves the app
  4. User returns to the app
  5. Cognito calls sync()
  6. sync() looks for keys in AsyncStorage, but they're not there

So to fix, I either need to store the keys—and just the keys, not the values—in AsyncStorage or I need to store them somewhere else and retrieve them in the sync() method.

A quick test with storing them in AsyncStorage works locally, but I haven't tried it on a device yet. I just added this to the top of setItem():

        AsyncStorage.setItem(MYSTORAGE_KEY_PREFIX + key, value); 

Probably not what I'll go with when I ship it, but at least I don't have to log in again every time I refresh the app on my emulator for right now. Tomorrow, I'll figure out a good way to store these keys in Keychain for consistency.


UPDATE: In a major face-palm moment, I figured out why I thought I had it working previously. It's because I tested on my emulator with the default storage. Since that's AsyncStorage, all the keys did get into AsyncStorage. So then when I re-added my custom storage class, it worked! So exciting! Except it was all lies.

The fix I mentioned on April 4 is still needed because of the way I'm querying my own backend service for user data. You can see my confusion on April 8 when it suddenly was only working on one emulator. I now believe that's because I spun up a new iOS emulator so it stopped working there, but kept working on Android.

samih7 commented 3 years ago

@nerdygirl @rcCaregiven @Goszu @therealemjy Are you guys also reproducing the issue on v4.x.x?

Goszu commented 3 years ago

@samih7 v4 blows up with Expo:

The package at "node_modules/crypto-js/core.js" attempted to import the Node standard library module "crypto". It failed because the native React runtime does not include the Node standard library. Read more at https://docs.expo.io/workflow/using-libraries/#using-third-party-libraries

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

chrisbonifacio commented 3 years ago

Not stale bot, still looking into this. Thank you @Goszu, @samih7, and @nerdygirl for your patience and the details you've provided us thus far. Apologies for the delay.

Khairo-kh commented 3 years ago

Thanks everyone for all the details. I am looking into this and I don't seem to be able to reproduce this issue. I tried different auth flows (default and custom) with both default and custom storage solutions and there is no problem with maintaining the current session even after many reloads (or exiting the app and coming back). Auth.currentSession() and Auth.currentAuthenticatedUser() always work as expected. Does the issue occur consistently after specific actions? if so, can anyone provide a little more details on how exactly is it reproduced?

rcCaregiven commented 3 years ago

We did end up resolving this on our end. It had to do with how we were returning promises (or how/when they resolved) after replacing AsyncStorage with react-native-keychain. It has all been refactored since our initial fix, so unfortunately I don't have better details than that.

joebernard commented 3 years ago

We see this issue occasionally. It is rare but does happen. I have not been able to pin it down to a particular user action. They open their device and find themselves logged out.

samih7 commented 3 years ago

We see this issue occasionally. It is rare but does happen. I have not been able to pin it down to a particular user action. They open their device and find themselves logged out.

Exactly the same than @joebernard for us, except it is not that rare. We also removed the custom auth storage layer we had implemented, so we now use plain AsyncStorage that comes with aws-amplify. But yes, the worst part is that we can't reproduce the issue consistently either.

@Khairo-kh In another context we tried to call currentCredentials from @aws-amplify/auth and we received a No Cognito Identity pool provided for unauthenticated access error. Makes sense since we indeed didn't have an identity pool configured, but could this be related to the logout issue in any way?

Khairo-kh commented 3 years ago

@samih7 I think this is an expected response if no identity pool is configured. Identity pools are used more on the authorization side of things. This can be something like authorizing authenticated users to access certain AWS services (S3 storage for example), or allowing unauthenticated access to certain resources. The issue described here seems to be more about maintaining sessions after authenticating a user (with user pools) so I don't think this is related. Still looking into this, thanks everyone for the feedback.

zzBrunoBrito commented 2 years ago

Any news on that? I'm facing the same issue here

jwhiteley89 commented 2 years ago

Any update from aws or a potential solution??? Using the amplify authentication wrapper in a react application, Im currently having this issue on only 2 machines out of many where the user has to login every day or every few days. However, all the other machines will remain logged in for the default of 30 days.

I have also tried using a custom storage class like stated in the docs to no avail.

jwhiteley89 commented 2 years ago

We see this issue occasionally. It is rare but does happen. I have not been able to pin it down to a particular user action. They open their device and find themselves logged out.

Exactly the same than @joebernard for us, except it is not that rare. We also removed the custom auth storage layer we had implemented, so we now use plain AsyncStorage that comes with aws-amplify. But yes, the worst part is that we can't reproduce the issue consistently either.

@samih7 Have you noticed the issue only stays with the computer and not the user?? Im having this issue on 2 machines out of 20. I have everyone using the same credentials to login but this issue will only happen on the same 2 machines no matter who uses them.

Goszu commented 2 years ago

I think I found out what was causing it in my case. I am using expo-secure-store as a storage solution. It does not accept certain characters inside keys (only accepts ".", "-", and "_"). In my setup phone number is being used as a user name. It is a part of the key for session details. Phone number includes "+" sign. That basically throws an error and session information is not persisted.

samih7 commented 2 years ago

We see this issue occasionally. It is rare but does happen. I have not been able to pin it down to a particular user action. They open their device and find themselves logged out.

Exactly the same than @joebernard for us, except it is not that rare. We also removed the custom auth storage layer we had implemented, so we now use plain AsyncStorage that comes with aws-amplify. But yes, the worst part is that we can't reproduce the issue consistently either.

@samih7 Have you noticed the issue only stays with the computer and not the user?? Im having this issue on 2 machines out of 20. I have everyone using the same credentials to login but this issue will only happen on the same 2 machines no matter who uses them.

Hard to say in our case. The same users tend to get logged out, but it could indeed be device-related.

I think I found out what was causing it in my case. I am using expo-secure-store as a storage solution. It does not accept certain characters inside keys (only accepts ".", "-", and "_"). In my setup phone number is being used as a user name. It is a part of the key for session details. Phone number includes "+" sign. That basically throws an error and session information is not persisted.

For us it's definitely not a matter of custom storage — we removed that layer and the issue was still occurring.

angusmccloud commented 2 years ago

I think I found out what was causing it in my case. I am using expo-secure-store as a storage solution. It does not accept certain characters inside keys (only accepts ".", "-", and "_"). In my setup phone number is being used as a user name. It is a part of the key for session details. Phone number includes "+" sign. That basically throws an error and session information is not persisted.

@Goszu - did you find a solution to this?

Goszu commented 2 years ago

@angusmccloud yes. If you are using custom storage solution (like 'expo-secure-store') and it does not accept certain chars in keys, try switching to AsyncStorage for example. Another option is to write a bit of code that will replace unsupported characters when saving, retrieving and deleting storage items.

Andrea-Vigano commented 1 year ago

Any updates on the issue?

We are experiencing a similar behavior with some users getting logged out from time to time (some days to some minutes after login)

No custom storage solution is being used

angusmccloud commented 1 year ago

@Andrea-Vigano - I don't remember specifics (this was almost a year ago), but looking at my project...

On login/sign-up I store credentials in the secure store, and stringified:

const credentials = { email, password: pwd };
await SecureStore.setItemAsync("auth", JSON.stringify(credentials));

Then when looking to see if the user is logged in, I first check to see if Amplify things they're signed in: const user = await Auth.currentAuthenticatedUser();

If they're not, I try to log them in with the secureStore:

const credentials = await SecureStore.getItemAsync('auth');
if(credentials) {
  const { email, password } = JSON.parse(credentials);
  const user = await Auth.signIn(email, password);
  const formattedUser = await formatAuthUser(user);
  return formattedUser;
} else {
  return unauthedUser;
}

Note: There are try/catches and things wrapped around each of these sections of code, etc...