Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: Wrapped error: User not authenticated #318

Open
alphec opened this issue Mar 24, 2020 · 60 comments
Open

Android: Wrapped error: User not authenticated #318

alphec opened this issue Mar 24, 2020 · 60 comments

Comments

@alphec
Copy link

alphec commented Mar 24, 2020

I get this error when trying to retrieve credentials stored in the keychain on Android devices. IMHO this is the effect of following exception being thrown: UserNotAuthenticatedException.

This is my code:

  useEffect(() => {
    if (!loginStarted) {
      return;
    }

    Keychain.getSupportedBiometryType()
      .then(data => {
        console.log('Supported biometry: ' + data);
        setBiometry(!!data);
        if (!!data) {
          // Try auto login
          Keychain.getGenericPassword({service: service})
            .then(credentials => {
              if (!credentials) {
                // no credentials stored, continue with normal login
                console.log('No saved credentials.');
                return;
              }

              // auto login with stored credentials
              if (credentials.username && credentials.password) {
                console.log('Trying auto login with saved credentials: ' + JSON.stringify(credentials));
                login(credentials.username, credentials.password);
              }
            })
            .catch(e => console.log('Error getting credentials: ' + e));
        }
      })
      .catch(e => console.log('Error getting biometry: ' + e));
  }, [loginStarted]); 

The error is thrown at Keychain.getGenericPassword. On iOS the code works fine. Any idea or workaround for this?

@skicson
Copy link

skicson commented Apr 1, 2020

I noticed similar behavior where the onAuthenticationSucceeded was being called (in KeychainModule.java but I was still getting the UserNotAuthenticatedException. Basically the decryption key's authentication was expiring before the cipher could get initialized. (probably since I was running in a debug environment, but could also happen with slower devices)

I was able to get it to work by extending the UserAuthenticationValidityDuration attribute of the key from 1 second to 5. In my case, it was using the RSA key, so the change was in the CipherStorageKeystoreRsaEcb.java file, around line 228

    return new KeyGenParameterSpec.Builder(alias, purposes)
      .setBlockModes(BLOCK_MODE_ECB)
      .setEncryptionPaddings(PADDING_PKCS1)
      .setRandomizedEncryptionRequired(true)
      .setUserAuthenticationRequired(true)
      .setUserAuthenticationValidityDurationSeconds(5)
      .setKeySize(ENCRYPTION_KEY_SIZE);

@hoangcxa
Copy link

hoangcxa commented Apr 3, 2020

I'm having the same problem with android Samsung s9.
I'm using this method to save credentials.
setInternetCredentials(server, username, password, { accessControl: ACCESS_CONTROL.BIOMETRY_ANY, });
When I'm using this method getInternetCredentials(server) to get credential, and using fingerprint option, I can get my username and password back. But when I use face Id option, even the faceId verified. I still can't get my username and password (undefined) , and it's showing in warn "Wrapped error: User not authenticated " .

@lucaashrq
Copy link

Any update for this issue?
I'm having the same problem after i updated for RN 0.62.x

@hoangcxa
Copy link

hoangcxa commented Apr 10, 2020

@skicson I tried your way but it's still doesn't work. When I'm using fingerprint , it works normal. But when I use Face Recognization of this device, it's still throw that error.
2020-04-10 11:14:43.034 5729-6663/com.awesomeproject V/myLog CipherStorageBase: failed:: User not authenticated android.security.keystore.UserNotAuthenticatedException: User not authenticated at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1584) at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1714) at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54) at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89) at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265) at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109) at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984) at javax.crypto.Cipher.tryCombinations(Cipher.java:2891) at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796) at javax.crypto.Cipher.chooseProvider(Cipher.java:773) at javax.crypto.Cipher.init(Cipher.java:1143) at javax.crypto.Cipher.init(Cipher.java:1084) at com.oblador.keychain.cipherStorage.CipherStorageBase$Defaults.lambda$static$1(CipherStorageBase.java:539) at com.oblador.keychain.cipherStorage.-$$Lambda$CipherStorageBase$Defaults$gUPKtklt7huSpCITAtk3nzsSgTY.initialize(Unknown Source:0) at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:393) at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:338) at com.oblador.keychain.KeychainModule$InteractiveBiometric.onAuthenticationSucceeded(KeychainModule.java:856) at androidx.biometric.BiometricFragment$2$2.run(BiometricFragment.java:140) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:919)

@hoangcxa
Copy link

@skicson regarding to the stack above, the onAuthenticationSucceeded is being called . But then some how the public static final DecryptBytesHandler decrypt = (cipher, key, input) -> { cipher.init(Cipher.DECRYPT_MODE, key); }; failed with the android.security.KeyStore.getInvalidKeyException

@krutkowski86
Copy link

+1

@sh-rp
Copy link

sh-rp commented Apr 27, 2020

I have not been able to test with a fingerprint enabled device, but I can confirm the same wrapped exception being thrown on a google pixel 4 with face recognition enabled. iOS works fine.

@cold-briu
Copy link

cold-briu commented Apr 28, 2020

same error here!
Pixel 3 fingerprint.

I tried...

setUserAuthenticationValidityDurationSeconds

and it didn't worked for me

@matthewyeager
Copy link

I'm also noticing this issue with Android. It works correctly until the device is restarted.

The weird thing is, setUserAuthenticationValidityDurationSeconds seems to be related to the time since the user authenticated from the lock screen. If I call getGenericPassword after unlocking the device within setUserAuthenticationValidityDurationSeconds value, it works.

@Sandi89
Copy link

Sandi89 commented May 9, 2020

Hi guys, i am also experiencing this issue with Android 8. Fingerprint works fine, so does Iris authentication. Face ID fails every single time. It would be great if there was a setting to force which type of biometric to use to unlock keychain as on Android it falls back to whatever user selected in the preference.

@crees1
Copy link

crees1 commented Jun 4, 2020

Has anyone found a workaround to get this to work on devices that have both Fingerprint as well as Face authentication? Currently I just have biometrics disabled on Android until I can figure out a way to either make Face work on Android or force only Fingerprint for the time being.

@ericrguimaraes
Copy link

ericrguimaraes commented Jul 24, 2020

I was getting the same error on a Pixel 3 emulator. So I went to Settings and removed the fingerprint, then set it up back again. After that, I got rid of the error. I know it's maybe too much to ask of your users (to remove then set up again the fingerprint in android settings), but it's a possible workaround.

@ericrguimaraes
Copy link

I was getting the same error on a Pixel 3 emulator. So I went to Settings and removed the fingerprint, then set it up back again. After that, I got rid of the error. I know it's maybe too much to ask of your users (to remove then set up again the fingerprint in android settings), but it's a possible workaround.

I just noticed that, after restarting the phone, the error comes back :/

@calz10
Copy link

calz10 commented Jul 27, 2020

hi folks, i have updated rn-key-chain package thought that its because im using expo and i have old keychain version, yet
im still getting Wrapped error: unknown key type passed to RSA error. FYI, IOS works well, and in android, without prompting any types it will successfully get the secret, so currently, im using rn-touchid for prompting biometrics in android and get data in my rn-keychain.

@timxyz
Copy link

timxyz commented Aug 13, 2020

Increasing setUserAuthenticationValidityDurationSeconds to a larger value fixes this for me. Note that you have to ensure you have this value set before you store the secret into the keychain.

With that in mind I think #339 is a reasonable fix for this.

@whysetiawan
Copy link

any update on this?

@skicson
Copy link

skicson commented Aug 18, 2020

I was able to get Face ID working consistently (on a Pixel 4) when I extended the setUserAuthenticationValidityDurationSeconds to 60. Logged out, killed the app, logged back in to set the password (and presumably the duration). For the Face ID, it seems the time between when the "confirm" dialog appears and when the user selects "confirm" has to be less than setUserAuthenticationValidityDurationSeconds for it to work.

@vdino96
Copy link

vdino96 commented Sep 14, 2020

any update?

@Zo2m4bie
Copy link

Zo2m4bie commented Sep 22, 2020

This PR solves the issue #399

@ameenmattar
Copy link

Any updates ?

@Zo2m4bie
Copy link

Zo2m4bie commented Oct 10, 2020

@ameenmattar you can use my fork

@AdriaRios
Copy link

Any update?

@CWolfs
Copy link
Contributor

CWolfs commented Nov 30, 2020

So I've been doing a little digging on this issue for the Pixel 4 / Face scan only devices.

I agree with what @skicson said. The setUserAuthenticationValidityDurationSeconds time seems to start after the succesful Face scan. If the user doesn't select 'Confirm' before that countdown is over - they will get the User not authenticated error.

I know some people extended the time in the setUserAuthenticationValidityDurationSeconds call which can work but I wanted to see if there was another solution.

I found this PromptInfo.Builder method call setConfirmationRequired. This call removes the 'Confirm' button on Android 10+ but it's also only classed as a 'hint' to the vendor version of Android OS so I don't know how well it's supported.

E.g. As outlined in this blog post - Android - One Biometric API Over All.

With confirm button

to

Without confirm button

This could be worth investigating more. If this works fine I'll make a PR for the change.

@CWolfs
Copy link
Contributor

CWolfs commented Nov 30, 2020

Seems to work fine on the Pixel 4 (need to test other devices) and it's actually more inline with the behaviour/experience seen on iOS with FaceID too.

@Zo2m4bie
Copy link

Zo2m4bie commented Dec 1, 2020

@CWolfs could you ping me here in this thread when your PR is ready? I will be able to check it on Samsung with FaceID

@CWolfs
Copy link
Contributor

CWolfs commented Dec 1, 2020

@Zo2m4bie sure, I'll try to do in the next few days - been bogged down with wrapping up a release but want to PR this soon. I only had a Pixel 4 to test so if you can test this on a Samsung that would be great to see if it works or not. I'll ping you when I'm ready.

@sgal
Copy link
Contributor

sgal commented May 7, 2021

@tunm1228 What exactly are you doing and what exactly is not working?

@tunm1228
Copy link

tunm1228 commented May 7, 2021

@sgal I want biometric authentication when calling the function Keychain.getGenericPassword(). IOS working fine.

@sgal
Copy link
Contributor

sgal commented May 7, 2021

@tunm1228 Sorry, I cannot help you if you don't provide the details of your implementation and error you're getting. I would suggest trying the example app and see if that is working.

@AlphaJuliettOmega
Copy link

@sgal Android Keychain behaves very differently for Android >8 and Android <8

If you've tested for both, then I might test to see if it works for us too.

At the moment there's a False Acceptance and False Rejection problem for Android <=8

ie. the Keychain responds 'Succesful unlock' but with empty string content.
or the Keychain master responds with 'Failed to unlock' for seemingly no reason, in an unreproducible way.

@sgal
Copy link
Contributor

sgal commented May 7, 2021

@AlphaJuliettOmega I only tested on Android 9, 10 and 11. Which options do you use?

@AlphaJuliettOmega
Copy link

@sgal it's got to do with this blog post:
https://security.googleblog.com/2018/06/better-biometrics-in-android-p.html

False Acceptance Rate + False Rejection Rate

I don't know how to deal with this in the library but you might.

Android <9 ie. 8 and older needs this config:

  accessControl: Keychain.ACCESS_CONTROL.DEVICE_PASSCODE,

A bit related:
#262

Android 9+ can use:

accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE,

Any mention of biometry in the config causes older phones to basically ask for your fingerprint, and it responds that the fingerprint was successfully entered, but

  • sometimes the keychain remains locked
  • sometimes the keychain unlocks and responds with empty strings

@arpitgarg23
Copy link

arpitgarg23 commented May 21, 2021

Even with RN 0.64 upgrade getting both errors with 7.0.0 key chain on android

2021-05-21 12:28:22.970 32466-32556/com.xx.xx W/CipherStorageBase: User not authenticated
    android.security.keystore.UserNotAuthenticatedException: User not authenticated
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:895)
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:937)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
        at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
        at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
        at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
        at javax.crypto.Cipher.init(Cipher.java:1143)
        at javax.crypto.Cipher.init(Cipher.java:1084)
        at com.oblador.keychain.cipherStorage.CipherStorageBase$Defaults.lambda$static$1(CipherStorageBase.java:519)
        at com.oblador.keychain.cipherStorage.-$$Lambda$CipherStorageBase$Defaults$DeW6NXOzsQTAPQNNW0rqTXPHW4c.initialize(Unknown Source:0)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:377)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:332)
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.decrypt(CipherStorageKeystoreRsaEcb.java:127)
        at com.oblador.keychain.KeychainModule.decryptToResult(KeychainModule.java:669)
        at com.oblador.keychain.KeychainModule.decryptCredentials(KeychainModule.java:636)
        at com.oblador.keychain.KeychainModule.getGenericPassword(KeychainModule.java:296)
        at com.oblador.keychain.KeychainModule.getGenericPasswordForOptions(KeychainModule.java:357)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
        at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
        at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
        at android.os.Looper.loop(Looper.java:193)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
        at java.lang.Thread.run(Thread.java:764)
2021-05-21 12:28:22.973 32466-32556/com.xx.xx D/CipherStorageBase: Unlock of keystore is needed. Error: User not authenticated
    android.security.keystore.UserNotAuthenticatedException: User not authenticated
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:895)
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:937)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
        at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
        at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
        at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
        at javax.crypto.Cipher.init(Cipher.java:1143)
        at javax.crypto.Cipher.init(Cipher.java:1084)
        at com.oblador.keychain.cipherStorage.CipherStorageBase$Defaults.lambda$static$1(CipherStorageBase.java:519)
        at com.oblador.keychain.cipherStorage.-$$Lambda$CipherStorageBase$Defaults$DeW6NXOzsQTAPQNNW0rqTXPHW4c.initialize(Unknown Source:0)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:377)
        at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:332)
        at com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb.decrypt(CipherStorageKeystoreRsaEcb.java:127)
        at com.oblador.keychain.KeychainModule.decryptToResult(KeychainModule.java:669)
        at com.oblador.keychain.KeychainModule.decryptCredentials(KeychainModule.java:636)
        at com.oblador.keychain.KeychainModule.getGenericPassword(KeychainModule.java:296)
        at com.oblador.keychain.KeychainModule.getGenericPasswordForOptions(KeychainModule.java:357)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
        at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
        at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
        at android.os.Looper.loop(Looper.java:193)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
        at java.lang.Thread.run(Thread.java:764)

@AlphaJuliettOmega
Copy link

AlphaJuliettOmega commented May 21, 2021

@arpitgarg23 yes, exactly!

Did you try the config settings I've suggested in the comment before yours?

Here's a snippet that might help:

let androidConfig: Keychain.Options = {
  accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE,
  // https://github.com/oblador/react-native-keychain/issues/262
  storage: Keychain.STORAGE_TYPE.AES
};

/**
 * This config is for Android 7 and 8 (Api level 26 and below) devices, which report false authentications and false authentication rejections
 * ie. Biometric auth passes but with an empty result
 * or fails when it should pass
 * so we bypass Biometric for them.
 */
const androidLegacyConfig: Keychain.Options = {
  accessControl: Keychain.ACCESS_CONTROL.DEVICE_PASSCODE,
  // https://github.com/oblador/react-native-keychain/issues/262
  storage: Keychain.STORAGE_TYPE.AES
};

const androidLegacyCheck = () => {
  if (Platform.OS === 'android') {
    let androidVersion = device.getAndroidVersion();
    if (androidVersion !== 0) {
      if (androidVersion <= 26) {
        androidConfig = androidLegacyConfig;
      }
    }
  }
};
...
 await Keychain.setGenericPassword(
      username,
      password,
      Platform.OS === 'ios' ? iosUnlockConfig : androidConfig
    );
...
await Keychain.getGenericPassword(
    Platform.OS === 'ios' ? iosUnlockConfig : androidConfig
  )

@arpitgarg23
Copy link

@AlphaJuliettOmega Thanks for the snippet.
I was using accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY irrespective of Platform or android. I tried the code my android api level is 28 so below configuration was applied ```
{ accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE,
// #262
storage: Keychain.STORAGE_TYPE.AES
}
removed the warnings.

@sgal
Copy link
Contributor

sgal commented May 21, 2021

@arpitgarg23 @AlphaJuliettOmega Biometrics are not used for AES storage, only RSA, so ACCESS_CONTROL value has no difference here.

@AlphaJuliettOmega
Copy link

@sgal

Biometrics are not used for AES storage, only RSA

How do you know this / where can I read more?

Access control here, set to any other values for Android <9 causes keychain retrieval failures, regardless of the storage type.

@sgal
Copy link
Contributor

sgal commented May 24, 2021

@AlphaJuliettOmega I meant in this library. In Android API you can make AES key that is protected by biometrics or device passcode. This lib though only offers that for RSA keys.

This is done by using setUserAuthenticationRequired(true) on KeyGenParameterSpec object that is used to generate crypto keys.

This is how it is done in this lib for RSA storage - https://github.com/oblador/react-native-keychain/blob/master/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreRsaEcb.java#L228

And this is for AES - https://github.com/oblador/react-native-keychain/blob/master/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesCbc.java#L187

Regarding your case - let me debug the flow with access controls to see what could be the culprit. Meanwhile, is there any pattern in device models that fail more often?

@sgal
Copy link
Contributor

sgal commented May 24, 2021

@AlphaJuliettOmega Ok, I think I found the issue(s).
First of all, a bug in getGenericPassword ignores the storage type in options when passed along with access control that implies biometrics.
The second is the automatic storage upgrade, which re-stores your values when more secure storage is available (based on the access control value). This can be prevented by not passing the access control to getGenericPassword method.

I suggest you skip all options in the getGenericPassword call, except for the service value (if you use it). Leave setGenericPassword as is.

@AlphaJuliettOmega
Copy link

@sgal that's very interesting feedback, unfortunately the settings I documented above isn't working for all Android devices.

React Native Keychain 7.0.0

Analytics reports .setGenericPassword failing for these device models:

  • Model, Android Version
  • Xiaomi Redmi 9A, 10
  • Xiaomi Redmi 9C, 10
  • Redmi Note 8, 10
  • Huawei Y5, 9
  • Huawei P8 Lite, 7.0
  • Huawei P9 Lite, (5.1.1 & 7)
  • Huawei P10, 9
  • Huawei P20 Lite, 9
  • Huawei P20 Pro, 10
  • Huawei P30, 9
  • Huawei P30 Lite, 10
  • Huawei P40 Lite, 10
  • Huawei P Smart, 9
  • Huawei P Smart Pro, 9
  • Huawei Y3, 8.1.0
  • Huawei Y5 Lite, 8.1.0
  • Huawei Y7, 8.1.0
  • Huawei Y7 Prime, 10
  • Huawei Y9 Prime, 9
  • Samsung Galaxy J2, 5.1.1
  • Samsung Galaxy J2 Core, 8.1.0
  • Samsung Galaxy J4 Core, 8.1.0
  • Samsung Galaxy J4+, 9
  • Samsung Galaxy A5, 7.0
  • Samsung Galaxy S6 Edge, 7
  • Samsung Galaxy J8, 10
  • Samsung Galaxy S8, 9
  • Samsung Galaxy S9, 10
  • Samsung Galaxy A10s, 10
  • Samsung Galaxy S10, 11
  • Samsung Galaxy S10e, 9
  • Samsung Galaxy S10+, 11
  • Samsung Galaxy A12, 10
  • Samsung Galaxy A20, 10
  • Samsung Galaxy S20 Ultra, 11
  • Samsung Galaxy S21, 11
  • Samsung Galaxy A21s, 10
  • Samsung Galaxy A30s, 10
  • Samsung Galaxy A31, 10
  • Samsung Galaxy A32, 11
  • Samsung Galaxy A50, 9
  • Samsung Galaxy A51, 10
  • Samsung Galaxy A70, 11
  • Samsung Galaxy A71, 11
  • Samsung Galaxy Note 9, 10
  • Samsung Galaxy Note 10, (android 10 and 11 recorded failing)
  • Samsung Galaxy Note 10 Lite, 11
  • Samsung Galaxy Note 10+, 11
  • Samsung Galaxy Note 20 Ultra, 11
  • Oppo A15, 10
  • Vivo Y20, 10
  • Hisense U962, 8.1.0
  • Hisense U40 Lite, 8.1.0
  • Hisense F25, 8.1.0
  • Hisense Infinity E30 lite, 9
  • Mobicel R6 Plus, 9
  • Mobicel V4, 8.1.0
  • Mobicel CLIK, 8.1.0
  • Sony Xperia XA1, 8.0.0
  • Tecno POP 2X AIR, 8.1.0

As well as on iOs (much more rarely):

  • iPhone 11 Pro | 14.2
  • iPhone 7 | 14.4.2

@sgal
Copy link
Contributor

sgal commented May 26, 2021

@AlphaJuliettOmega I see. Could you try removing all options from getGenericPassword call? Except for service, of course.

@BhanaviShukla
Copy link

BhanaviShukla commented Jun 2, 2021

I was getting the same error on a Pixel 3 emulator. So I went to Settings and removed the fingerprint, then set it up back again. After that, I got rid of the error. I know it's maybe too much to ask of your users (to remove then set up again the fingerprint in android settings), but it's a possible workaround.

I just noticed that, after restarting the phone, the error comes back :/

I am at the exact same point @ericrguimaraes - it works if I reset the fingerprint in the device settings, then stops if I restart. Did you find a solution to the issue?

@ericrguimaraes
Copy link

ericrguimaraes commented Jun 2, 2021

I was getting the same error on a Pixel 3 emulator. So I went to Settings and removed the fingerprint, then set it up back again. After that, I got rid of the error. I know it's maybe too much to ask of your users (to remove then set up again the fingerprint in android settings), but it's a possible workaround.

I just noticed that, after restarting the phone, the error comes back :/

I am at the exact same point @ericrguimaraes - it works if I reset the fingerprint in the device settings, then stops if I restart. Did you find a solution to the issue?

Not quite. I actually worked around it: on Android, I use react-native-keychain to store the token encrypted but not protected by biometrics (Keychain.STORAGE_TYPE.AES). In addition to that, I use another lib, expo-local-authentication, to ask the user for her biometrics and check that she's the one using the app. If this check succeds, I then fetch the token by using react-native-keychain.

I would add that I am no security expert, however this implementation suffices for my specific needs.

@florinleu
Copy link

Is there a way to add back the ability to use device passcode/pattern/password fro prompt info in case it doesn't have touch/face id? There are lots of Android devices that still don't have biometrics and it's really annoying that they're not supported at the moment.

@AlphaJuliettOmega
Copy link

@florinleu It doesn't seem that's possible, or I don't understand how to yet

(Specifically for Api level < 25 &/| Android <=8

if you set the security type to DEVICE_PASSCODE
those devices can then access the Keychain, (doesn't throw error in the ticket title)
but it doesn't actually ask for the device passcode, which is fine in my use case - pin in my app, stored encrypted in the keychain

@sgal removing all options does not work for my use case, it makes react-native-keychain smart upgrade the storage and auth type to Biometrics, causing the keychain to become inaccessible

to be clear, my device 'does support' biometrics, has a fingerprint reader, but attempting to access the keychain with biometrics causes it to fail.

The spookiest detail:
If I remove and add my fingerprint in device Settings, keychain authentication/access works ONCE, and that simple detail has muddied the waters in my testing before, and I'm sure for many others too.

@ericrguimaraes
Copy link

ericrguimaraes commented Feb 1, 2022

I can confirm that the issue still happens as of react-native-keychain@8.0.0.

I just created a new React Native sample app from scratch (npx react-native init AwesomeProject --version 0.65.1) and installed the lib. Then I edited App.js to make it look like this:

import React from 'react';
import {Button, View} from 'react-native';
import * as Keychain from 'react-native-keychain';

const username = 'johndoe1';
const password = 'password1234';
const service = 'com.example.testkeychain.' + username;

function onPressSave() {
  try {
    return Keychain.setInternetCredentials(service, username, password, {
      accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY,
      securityLevel: Keychain.SECURITY_LEVEL.SECURE_SOFTWARE,
      storage: Keychain.STORAGE_TYPE.RSA,
      rules: Keychain.SECURITY_RULES.NONE,
    });
  } catch (e) {
    console.warn(e.message);
  }
}

async function onPressRetrieve() {
  let ret;
  try {
    ret = await Keychain.getInternetCredentials(service, {
      authenticationPrompt: {
        title: 'Use fingerprint to read password',
        cancel: 'Cancel',
      },
    });
  } catch (e) {
    console.error('Exception: ' + e.message);
    return;
  }
  if (ret === false) {
    console.error('Credentials not found');
    return;
  }

  if (ret.username !== username) {
    // shouldn't ever happen:
    console.error('Retrieved username does not match expected');
    return;
  }

  console.log(JSON.stringify(ret));

  return ret.password;
}

const App = () => {
  return (
    <View>
      <Button title="Save password" onPress={onPressSave} />
      <Button title="Retrieve password" onPress={onPressRetrieve} />
    </View>
  );
};

export default App;

To reproduce the error:

  • Start android emulator (I tested on android 12 and 11 Pixel emulators)
  • Enroll fingerprint (in Android settings) and do NOT restart phone emulator
  • react-native start
  • react-native run-android
  • Wait for the app to load
  • Tap Save password
  • Tap Retrieve password
  • Provide fingerprint
  • Watch logged messages on console, password is retrieved correctly
  • Now restart the phone emulator (if you just close it and run it again, remember to use option -no-snapshot to the emulator.exe)
  • react-native run-android
  • Wait for app to open
  • Tap retrieve password
  • Provide fingerprint
  • Error! (see console)

One more thing:

  • Set up another (or the same) fingerprint on Android Settings (You can even remove this newly set up fingerprint)
  • Go back to the app
  • Tap retrieve password
  • Provide fingerprint
  • It works again!

And apparently it will keep working until you restart the phone again. Then it will break, and to fix it, do set up fingerprint again on Android Settings.

Now, I have no experience programming with the native part on Android, so I hope these steps might help someone else come up with a fix.

@sengerts
Copy link

Any updates on the issue? I feel like the is error is caused by a wrong implementation by Samsung and co. But there does not seem to be a patch for that yet.

@UgurGumushan
Copy link

UgurGumushan commented Jun 15, 2022

This is still unresolved and I'll have to disable the feature for Android because of this issue.
Version: react-native-keychain@8.0.0
Emulator: Pixel 3 API 30

@mousaormoses
Copy link

function LoadingScreen(): JSX.Element {
    const navigation = useNavigation();
    const [user, setUser] = useState(null);

    const checkUser = async (): Promise<void> => {
        try {
            await Auth.currentAuthenticatedUser({
                bypassCache: true
            });
            setUser(user);
            navigation.navigate('Page1');
        } catch (error) {
            console.log('User authentication failed:', error);
            navigation.navigate('Page2');
        }
    }
}

I created a page and named it "LoadingScreen" which is the first page to call when open the app
The issue I had was: I had Auth.currentAuthenticatedUser in another page too, and I just deleted it
this was the warning

WARN  Possible Unhandled Promise Rejection (id: 0):
"The user is not authenticated"

@YanislavRemitly
Copy link

Still have this issue on version 8.1.1.
Emulator: Pixel 6 API 33,
Emulator version: 32.1.12-9751036.
Android version: 13 (Tiramisu)

@gergof
Copy link

gergof commented Apr 25, 2024

This issue still happens on 8.2:

04-25 10:00:43.291 17186 23716 W CipherStorageBase: User not authenticated
04-25 10:00:43.291 17186 23716 W CipherStorageBase: android.security.keystore.UserNotAuthenticatedException: User not authenticated
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at android.security.KeyStore.getInvalidKeyException(KeyStore.java:895)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at android.security.KeyStore.getInvalidKeyException(KeyStore.java:937)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at javax.crypto.Cipher.init(Cipher.java:1143)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at javax.crypto.Cipher.init(Cipher.java:1084)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at com.oblador.keychain.cipherStorage.CipherStorageBase$Defaults.lambda$static$1(CipherStorageBase.java:533)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at com.oblador.keychain.cipherStorage.CipherStorageBase$Defaults$$ExternalSyntheticLambda1.initialize(Unknown Source:0)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:382)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at com.oblador.keychain.cipherStorage.CipherStorageBase.decryptBytes(CipherStorageBase.java:337)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at com.oblador.keychain.decryptionHandler.DecryptionResultHandlerInteractiveBiometric.onAuthenticationSucceeded(DecryptionResultHandlerInteractiveBiometric.java:95)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at androidx.biometric.BiometricFragment$9.run(BiometricFragment.java:907)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
04-25 10:00:43.291 17186 23716 W CipherStorageBase: 	at java.lang.Thread.run(Thread.java:764)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests