Skip to content

.read() return null since iOS 16.3.X even with the correct KeychainAccessibility #524

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

Closed
stefanschaller opened this issue Feb 16, 2023 · 15 comments · Fixed by #602 or #629
Closed

Comments

@stefanschaller
Copy link

stefanschaller commented Feb 16, 2023

Package version:

flutter_secure_storage: ^8.0.0

Flutter:

Flutter 3.3.10 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 135454af32 (vor 9 Wochen) • 2022-12-15 07:36:55 -0800
Engine • revision 3316dd8728
Tools • Dart 2.18.6 • DevTools 2.15.0

I have the following code:

  final _storage = const FlutterSecureStorage(
    aOptions: AndroidOptions(encryptedSharedPreferences: true),
    iOptions: IOSOptions(
      synchronizable: false,
      // https://support.apple.com/en-us/HT213407
      // https://stackoverflow.com/questions/10536859/ios-keychain-not-retrieving-values-from-background
      accessibility: KeychainAccessibility.first_unlock,
    ),
  );
  @override
  Future<String?> getAccessToken() async {
    final token = await _storage.read(key: "my_access_token");

   // The token is null, when the app was locked and we try to receive the token!! 😭

     return null;
  }

Regarding this question, the mode looks correct, but I just receive a null token when the app was moved into the background (not terminated) and the user locks the device.
https://stackoverflow.com/questions/10536859/ios-keychain-not-retrieving-values-from-background

Thanks for the support.

@saibotma
Copy link

Glad you opened this issue.
Spent a whole day debugging because of it.
My boss will kill me if it is not fixed until tomorrow! ;)

@gevork
Copy link

gevork commented Feb 27, 2023

Any updates?
I'm looking forward to solving this problem

@Stephnn0
Copy link

Stephnn0 commented Mar 5, 2023

I am having the same problem! any updates?

@alexandrim0
Copy link

You can try read it twice as this:
await _storage.read(key: "my_access_token");
final token = await _storage.read(key: "my_access_token");

I`m not sure if it is a kind of fix for prod but better than nothing :)

@T-moz
Copy link

T-moz commented Mar 23, 2023

@saibotma are you still alive ? 🤣
Did you manage to find a fix ?

@florentmorin
Copy link

florentmorin commented Apr 1, 2023

My boss will kill me if it is not fixed until tomorrow! ;)

😱 6 weeks with this bug! How many users are impacted?

Answer is in official documentation. There is a delay before protected data availability.

You can check protected data availability before accessing it. And, if protected data is not available, you can wait for notification.

And be sure to use the good protection level. And avoid access from main thread.

@VB10
Copy link

VB10 commented Apr 13, 2023

Is there anything else? Currently, there is no solution to the problem...

@florentmorin
Copy link

@VB10 If you wait for notification of availability when protected data is unavailable, keychain will return value instead of null.

@VB10
Copy link

VB10 commented Apr 13, 2023

When my users try to open from the lock screen, it returns to null, so it shows the latetly. Could you please let me know if you have any ideas about this issue. I've tried a lot of solutions, but nothing works. @florentmorin

@kadirbekar
Copy link

Phone status: Locked
Application: Killed

When tapping on notification, it wasn't able to read data from the secure storage while application starting.

For now we have updated accessibility options for IOS as following.

 final _storage = const FlutterSecureStorage(
     iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
  );
}

While the application is starting, it reads data from the secure storage.

@florentmorin
Copy link

You must verify that secured data is available by using dedicated function.

Keychain must be accessible after first unlock if you read it from background task.

And keychain mustn't be read from main thread.

@flodaniel
Copy link

@stefanschaller did you manage to work around this somehow?

@stefanschaller
Copy link
Author

@flodaniel I fixed it by one of the following methods. (Two different projects).

  1. Use a wrapper for the values:
  final _storage = const FlutterSecureStorage(
    iOptions: IOSOptions(
      synchronizable: false,
      accessibility: KeychainAccessibility.first_unlock,
    ),
  );

  final _cache = <String, String?>{};

  Future<String?> read({required String key}) async {
    if (_cache.containsKey(key)) {
      return _cache[key];
    }

    final result = await _storage.read(key: key);
    _cache[key] = result;
    return result;
  }
  1. Use an encrypted hive box: https://developerb2.medium.com/store-data-securely-with-hive-flutter-cbad35981880 instead of the keychain. (If you don't need the sync feature of the keychain)
      final encryptionKey = base64Url.decode(const String.fromEnvironment('secretKey'));
      
      final box = await Hive.openBox<String>(
        "name_of_the_box",
        encryptionCipher: HiveAesCipher(encryptionKey),
      );
      
      box.put(x, x);

secretKey is an env var, that is managed by GitLab Env Vars or the GitHub Secrets

@JoepHeijnen
Copy link

JoepHeijnen commented May 8, 2023

Do you guys know how to reproduce this issue? Only one colleague of mine can reproduce this issue once in a day or two and he is not a developer. He just opens the app and the issue occurs.

@bierbaumtim
Copy link
Contributor

I looked in the code and saw that the accessibility setting is ignored when reading or deleting values. It could also have an influence on writing data. I forked the repo and fixed that issue. Also, I changed the response type of most of the native methods, so now you get an PlatformException if the keychain operations failed. The exception contains the OSStatus code and the description provided by the operating system. This should make the logging and debugging much easier.
Additionally I included the change from PR #536 to move the keychain operations into a seperate non-ui thread.

This additions combined with custom plugin to check when keychain is available fixed the problem for me.

Fork is available under https://github.com/bierbaumtim/flutter_secure_storage .
Gist for the plugin code: https://gist.github.com/bierbaumtim/63c5057fc2889558d1801d95d93b6c6c

I hope this helps some of you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment