expo: Could not decrypt the item in SecureStore

Environment

Pixel, Android version 27 with Expo SDK 25.0.0 - Standalone.

Error

Could not decrypt the item in SecureStore when decoding a users preserved password.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 7
  • Comments: 57 (25 by maintainers)

Most upvoted comments

It happened to me as well, only on Android so far. All I am saving is the users credentials so I can login using face or fingerprint scan. I’m using ExpoKit 32.

I think this needs to be re-opened

have the same issue: i’ve delete from phone old version of app and install new. no SDK update between versions.

Here is how we resolved.

try {
      token = await SecureStore.getItemAsync('token');
    } catch (e) {
      // this only throws if they reinstall on android
      await SecureStore.deleteItemAsync('token');

        // display an alert: "Cached data from a previous installation was detected and
        // needed to be removed prior to allowing login. Please login again."
        }
      });
    }

Rather than having users of SecureStore do this and force the error to throw first it would be good to handle this inside of SecureStore as mentioned farther up the issue.

Commenting just to keep the thread updated w/ latest SDK versions. This is still occurring in SDK 35.

Feature request created here. Lets vote for this fix.

FWIW, this is the code we have to deal with this

// SecureStorage is not cleared when the app is uninstalled, but AsyncStorage _is_.
// So keep a key in AsyncStorage for tracking this, and if it's not there, clear all keys in SecureStorage
async function checkForFirstLaunch() {
  Sentry.addBreadcrumb({
    category: 'auth',
    message: 'Checking if first launch',
  });
  const { HAS_LAUNCHED_KEY, ...secureKeys } = storageConstants;

  try {
    const hasLaunched = await AsyncStorage.getItem(HAS_LAUNCHED_KEY);

    if (hasLaunched) {
      Sentry.addBreadcrumb({
        category: 'auth',
        message: 'Has launched app previously',
      });
    } else {
      Sentry.addBreadcrumb({
        category: 'auth',
        message: 'Launching the app for the first time, clear all keys',
      });
      console.log('Launching the app for the first time, clear all keys');
      await AsyncStorage.setItem(HAS_LAUNCHED_KEY, 'hello!');

      for (const key of Object.values(secureKeys)) {
        await SecureStore.deleteItemAsync(key);
      }
    }
  } catch (error) {
    Sentry.addBreadcrumb({
      category: 'auth',
      message: 'unable to check for first launch',
    });
    Sentry.captureException(error);
  }
}

The following fixed the issue for me on my device:

Go to the Settings Menu Click on Apps Find the app Click Clear Data

It seems like the keystore gets corrupted and SecureStore instead of cleaning it out just fails and does not empty it. Clear Data will clean out the corrupted data inside of it.

@ide My proposal: empty out the keystore when this case is hit. It would at least immediately fix the issue.

Same issue after upgrade from expo SDK 32 to 33

@ShMcK should this be reopened? lots of people seeing it.

But we’re not talking about blowing away the keystore at random. We’re taking about blowing away the keystore only when an error handling case is hit and developer’s apps are completely bricked and users cannot use them no matter how many times they uninstall / reinstall. This dependency is currently in production and is permanently bricking apps until each user manually clears the keystore. I think this deserves to be treated seriously or the package should be pulled so people know not to use it.

Thanks @shamilovtim I had a user followed the steps above and he’s able to login to the app without any issue. Yay! My concern is that most users who ran into this issue would not have contacted us. Thus they would not be able to access our app, even if they uninstall and reinstall the app. This is a very big issue.

+1

Let me continue with my story: I could fix it getting smaller the object I was storing, but, one day, it got bigger, and the issues appeared again.

So, I decided to: Encrypt the data by myself, save it in AsyncStorage (react-native), and save the decrypt keys in SecureStore. 🤷‍♂️

Good luck guys!

I had this issue immediately after upgrading to expo SDK 45 from SDK 44, testing in an eas development build. https://github.com/expo/expo/issues/1459#issuecomment-580468780 resolved the issue.

Closing the loop on this issue:

  • The expected behaviors on iOS and Android are presently different, as documented.
  • We would welcome either a feature request or (even better!) a PR to provide this same functionality on Android
  • An available workaround would be to store the data off the device, and identify the device by androidId, which is unique to each combination of app-signing key, user, and device.

Re-opening this but we don’t have any updates (when there is an update, this issue is where it will be posted).

@BrodaNoel would you recommend to use the same approach for persisting redux-state? We have a sensitive slice containing huge objects. cc @ide