Skip to content

[Android] No way to recover from INCOMPATIBLE_DEVICEclearCredentials() leaves the stale Keystore key, and clearAll() isn't bridged #1650

Description

@josh-long-raylo

Checklist

Description

[Android] No way to recover from INCOMPATIBLE_DEVICEclearCredentials() leaves the stale Keystore key, and clearAll() isn't bridged

On Android we're seeing a persistent login loop caused by CredentialsManagerException with code INCOMPATIBLE_DEVICE:

INCOMPATIBLE_DEVICE: This device is not compatible with the SecureCredentialsManager class.

It's thrown from NativeCredentialsManager.handleError (src/platforms/native/adapters/NativeCredentialsManager.ts:24) via CredentialsManagerError. This began for us immediately after upgrading to react-native-auth0 5.4.1, which pulled in auth0-android 3.14.0 and its tightened Keystore validation.

This matches the report in auth0/Auth0.Android#1016isDeviceIncompatible firing on Galaxy S24+/Z Fold5/Pixel at ~14,700 occurrences in 30 days. That issue was closed for inactivity rather than resolved.

Why it's unrecoverable from React Native

Our error handler catches the exception and calls clearCredentials(), expecting the user to re-authenticate with a fresh key. That can't work.

In auth0-android 3.21.0 — the version this SDK pins — SecureCredentialsManager.clearCredentials() removes seven named SharedPreferences entries and clears the biometric session. It does not touch the Android Keystore, and it does not remove the wrapped AES key material. CryptoUtil.getRSAKeyEntry() then returns the same existing alias on the next attempt (checking OLD_KEY_ALIAS first, then the package-prefixed KEY_ALIAS), so the fresh login hits the identical failure. The user is permanently stuck.

We can see this in our telemetry: affected users hit the recovery path repeatedly within the same session rather than once, and they carry the problem across app upgrades.

I think this method from the native Android SDK may be a work around to clear this:

auth0-android 4.0.0 added clearAll() (auth0/Auth0.Android#951), which calls crypto.deleteAllKeys() in addition to clearing storage. Storage.removeAll() was added in the same release (#918).

however clearAll() does not exist in android auth0 SDK 3.21.0 which the react native SDK is using.

As far as I can see there is currently no JS-reachable way to delete the key, and no combination of existing methods that achieves it. A potential workaround for us (using Expo) is a custom native module that calls KeyStore.deleteEntry() directly, which we'd rather avoid in case this is incompatible with future Auth0 versions.

Questions

  1. Is there a timeline for upgrading the pinned auth0-android dependency to 4.x?
  2. When that upgrade happens, will clearAll() be bridged and exposed through the JS CredentialsManager API?
  3. In the meantime, is there any supported way to clear the Keystore entry from React Native that we've missed?

Happy to supply any extra information if useful

Environment

  • react-native-auth0: 5.11.0
  • auth0-android (transitive): 3.21.0
  • React Native: 0.85.3 / Expo, Hermes, New Architecture
  • Platform: Android 16 (also observed on earlier versions)
  • Devices: Samsung flagships, Google Pixels etc.

Reproduction

We are unable to reproduce directly but our logs show this is happening mostly on android 16 when some users attempt to login and hit the error code INCOMPATIBLE_DEVICE

Additional context

No response

react-native-auth0 version

5.11.0

React Native version

0.86.0

Expo version

57.0.11

Platform

Android

Platform version(s)

15+ (mostly 16)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis points to a verified bug in the code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions