react-native-keychain: Android: Wrapped error: User not authenticated

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?

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 8
  • Comments: 58 (12 by maintainers)

Most upvoted comments

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.

7.0.0 was just released with the support of strong bio by default. Please try it out. It should remove all User not authenticated errors.

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.

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.

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.

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?

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);

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

@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, // https://github.com/oblador/react-native-keychain/issues/262 storage: Keychain.STORAGE_TYPE.AES } removed the warnings.

Hello @michaelgmcd right now the only available solution is using this fork. It has a bit different flow for Android and iOS, however it works for all devices

@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.

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.

@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