oblador / react-native-keychain

:key: Keychain Access for React Native
MIT License
3.12k stars 515 forks source link

Android: Wrapped error: User not authenticated #318

Open alphec opened 4 years ago

alphec commented 4 years ago

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?

BhanaviShukla commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

@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 commented 2 years ago

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:

One more thing:

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 commented 2 years ago

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 commented 2 years ago

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 commented 1 year ago
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 commented 4 months ago

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 commented 2 months ago

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)