Repository navigation
Android: users are logged out after updating to 5.11.2 or 5.11.3 from react-native-auth0 5.4.0 or older #1696
Description
Checklist
- The issue can be reproduced in the react-native-auth0 sample app (or N/A).
- I have looked into the Readme, Examples, and FAQ and have not found a suitable solution or answer.
- I have looked into the API documentation and have not found a suitable solution or answer.
- I have searched the issues and have not found a suitable solution or answer.
- I have searched the Auth0 Community forums and have not found a suitable solution or answer.
- I agree to the terms within the Auth0 Code of Conduct.
Description
After updating an app from react-native-auth0 3.2.1 to 5.11.2, every Android user is logged out on the first launch. getCredentials() rejects with CRYPTO_EXCEPTION ("A change on the Lock Screen security settings have deemed the encryption keys invalid and have been recreated...") and the stored credentials are deleted. iOS keeps the login.
It affects apps going straight to 5.11.2 or 5.11.3 from any react-native-auth0 version that bundles Auth0.Android 3.12.1 or older, which is every 3.x and 4.x release and 5.0.0 to 5.4.0. Those versions protect the credentials with an RSA key that only allows PKCS1 padding. Updating to 5.9.0 to 5.11.1 (Auth0.Android 3.20.0 and 3.21.0, both tested) migrates that key to OAEP and keeps the login. 5.11.2 (Auth0.Android 3.21.1) and 5.11.3 (3.21.2) delete it instead.
Expected: the login survives the update, as it does when updating to 5.11.1.
Cause, in Auth0.Android 3.21.1 (CryptoUtil.java at 3.21.1):
RSADecrypt:Cipher.initwith OAEP on the PKCS1-only key throwsInvalidKeyException(KeystoreINCOMPATIBLE_PADDING_MODE). Since fix(storage): authorize MGF1 digest to prevent INCOMPATIBLE_DEVICE on…3.x Auth0.Android#1048 this branch callsdeleteRSAKeys()anddeleteAESKeys()(L385-L386) before it throwsCryptoException.tryRecoverCurrentAESKeycatches it and callsattemptPKCS1Migration, which exists for exactly this key. The key was just deleted, sofindRSAKeyEntryreturnsnulland it logs "No RSA key found for migration".- The keys are deleted again (L648-L651), a new AES key is generated, the stored credentials can't be decrypted, and
SecureCredentialsManagerclears them.
Auth0.Android 4.2.0 has the same code (L364-L366). Before #1048, RSADecrypt in 3.20.0 turned the same exception into IncompatibleDeviceException without deleting anything, so the migration still found the key.
Why Auth0.Android's tests pass: the PKCS1 migration tests in CryptoUtilTest (shouldDetectAndMigratePKCS1KeyToOAEP, shouldAllowMultipleRetrievalsAfterMigration, shouldTriggerPKCS1MigrationWhenRSADecryptThrowsProviderException) stub RSADecrypt on the spy (L1761), so the real deletion never runs, and the key is still reported as present (L1763). shouldRecreateKeysAndThrowCryptoExceptionOnInvalidKeyExceptionWhenTryingToRSADecrypt (L901) tests the deletion on its own. No test combines the two, and on a device they always run together.
Suggested fix (in Auth0.Android): don't delete keys in RSADecrypt's InvalidKeyException / InvalidAlgorithmParameterException branch; only throw CryptoException. RSADecrypt's only caller is tryRecoverCurrentAESKey (L628), which already deletes both keys at L648-L651 when the PKCS1 migration doesn't succeed. Keys with a mismatched MGF1 digest (auth0/Auth0.Android#1016) would still be regenerated, and PKCS1 keys would be migrated first. A regression test could stub only Cipher.init, as the L901 test does, instead of RSADecrypt, and assert that getAESKey() returns the migrated key. react-native-auth0 would then need a 5.11.x release with the fixed Auth0.Android.
Reproduction
Reproduces every time.
With react-native-auth0 (our app, release builds, Android 16 emulator):
- Install the app built with react-native-auth0 3.2.1 (React Native 0.77.3), log in.
- Update it in place (
adb install -r) to the build with react-native-auth0 5.11.2 (React Native 0.88.0), launch. getCredentials()rejects withCRYPTO_EXCEPTIONand the user is back on the login screen. Updating the same way on iOS keeps the login.
With Auth0.Android only (no React Native, attached auth0-repro.zip). A minimal app that uses only SecureCredentialsManager, with a fake long-lived access token, so no login or network:
- Build it with
com.auth0.android:auth0:2.10.2, install, launch. It callssaveCredentials(new Credentials("", "access-token", "Bearer", null, inOneYear, "openid")). - Launch again:
getCredentials()returns the credentials. - Change the dependency to
3.21.1(only theSecureCredentialsManagerconstructor changes; 4.x also needs AGP 9 and compileSdk 37), update in place withadb install -r, launch.getCredentials()fails:CredentialsManagerException: A change on the Lock Screen security settings have deemed the encryption keys invalid and have been recreated. Any previously stored content is now lost. Please try saving the credentials again. Caused by: CryptoException: The encryption keys changed recently. You need to re-encrypt something first. - Launch again:
No Credentials were previously set.
Results with the minimal app (Android 16 / API 36 emulator; the starred rows were also run on Android 11 / API 30 and Android 12 / API 31, with the same results):
| Auth0.Android update path | react-native-auth0 with that version | Result |
|---|---|---|
| 2.10.2 → 3.21.1 ★ | 3.2.1 → 5.11.2 | Credentials deleted |
| 2.10.2 → 3.21.2 | 3.2.1 → 5.11.3 | Credentials deleted |
| 2.10.2 → 4.2.0 | – | Credentials deleted |
| 2.10.2 → 4.1.0 | 3.2.1 → 6.0.0-beta.0 | Migrated, credentials kept |
| 2.10.2 → 3.21.0 | 3.2.1 → 5.10.0 to 5.11.1 | Migrated, credentials kept |
| 2.10.2 → 3.20.0 ★ | 3.2.1 → 5.9.x | Migrated, credentials kept |
| 2.10.2 → 3.20.0 → 3.21.1 | 3.2.1 → 5.9.x → 5.11.2 | Kept (3.20.0 already migrated the key) |
| 3.21.1 only (save, then read) | 5.11.2 only | Works |
CryptoUtil log on the first launch after updating to 3.21.1 (Android 16, stack frames removed; our app logs the same sequence):
E/CryptoUtil: RSA key rejected the cipher parameters (likely MGF1 digest mismatch on Keystore2). Recreating key.
E/CryptoUtil: java.security.InvalidKeyException: Keystore operation failed
E/CryptoUtil: Caused by: android.security.KeyStoreException: Incompatible padding mode (internal Keystore code: -11 ...)
E/CryptoUtil: 2: Error::Km(r#INCOMPATIBLE_PADDING_MODE)) (public error code: 13 internal Keystore code: -11)
D/CryptoUtil: Deleting the existing RSA key pair from the KeyStore.
D/CryptoUtil: OAEP decryption failed, attempting PKCS1 migration
D/CryptoUtil: com.auth0.android.authentication.storage.CryptoException: The RSA key's authorized parameters are incompatible with the current cipher. The keys have been recreated; please retry.
D/CryptoUtil: No RSA key found for migration
W/CryptoUtil: Could not recover AES key. Deleting corrupted keys.
D/CryptoUtil: Deleting the existing RSA key pair from the KeyStore.
D/CryptoUtil: New AES key generated and stored
D/SecureCredentialsManager: Credentials were just removed from the storage
Android 11 logs the same sequence (KeyStoreException: Incompatible padding mode), so this isn't limited to Keystore2, even though the log message mentions it.
For comparison, Auth0.Android 3.20.0 on the same data:
E/CryptoUtil: The device can't decrypt input using a RSA Key.
D/CryptoUtil: OAEP decryption failed, attempting PKCS1 migration
D/CryptoUtil: PKCS1 migration successful - deleting old keys
D/CryptoUtil: Deleting the existing RSA key pair from the KeyStore.
D/CryptoUtil: AES key re-encrypted with OAEP and stored
Additional context
- Auth0.Android 4.2.0 (22 Oct) has the same bug, so a 6.x release that moves from 4.1.0 to 4.2.0 will log these users out too.
- The Auth0.Android 4.2.0 log follows the same path as 3.21.1, from "Recreating key" to "Credentials were just removed from the storage". Auth0.Android 5.0.0-beta.0 predates [Snyk] Upgrade react-native-screens from 4.5.0 to 4.6.0 #1092, but sync : Sync main to v5_developement Auth0.Android#1105 syncs
maininto the 5.x branch, so its next release will likely be affected too. - Our workaround until a fix ships: a one-time migration in
Application.onCreate, before React Native starts. If the RSA key is PKCS1-only, it decrypts the stored AES key with PKCS1, recreates the RSA key with the 3.21.1 spec (OAEP, MGF1 digests on API 35+), and re-wraps the AES key with OAEP. - We use
Auth0Providerwith the default storage, no biometrics (localAuthenticationOptions) anduseDPoP: false. Our code doesn't clear credentials whengetCredentials()fails; the SDK clears them before the promise rejects.
react-native-auth0 version
5.11.2 and 5.11.3 (updating from 3.2.1)
React Native version
0.88.0 (also reproduced without React Native, see Reproduction)
Expo version
No response
Platform
Android
Platform version(s)
Android 11 (API 30), 12 (API 31) and 16 (API 36), on emulators with Google APIs arm64 images
Activity
subhankarmaiti commentedon Oct 23, 2026
Thanks for reporting, @utkrishtsahu is working on it to fix the issue