Access Keychain values stored by ExpoSecureStore from a Flutter app [iOS]

1,723

I have finally solved the expo secure store access issue!

TL;DR

The most important things are that you set up the same keychain access group, same kSecAttrService (keychain service) and, of course, the key of the item you want to access.

Here is what are the preconditions:

  • you must set the keychain access group to the same value as in the react native's build, which is TEAM_ID.*. This can be achieved by one of the two options:

    • setting the Keychain Sharing capability to *, as shown on the screenshot below, or
    • open the entitlements file (project_dir/ios/Runner/Runner.entitlements) and add the item $(AppIdentifierPrefix)* or item TEAM_ID.* to the keychain access groups array. Notice that if You are adding the first item, there is no . before the *, since AppIdentifierPrefix is converted into TEAM_ID. before the build. Link to the apple docs on the keychain-access-groups entitlement. xCode keychain access group setup for react native / expo and flutter
  • If the app you are developing has the same bundle ID (i.e. it's an update to the existing ReactNatvie/Expo app), you should not have any problems with accessing the keychain items (when you do all the other preconditions, of course). Also, if the app has the same keychain sharing group, in this case TEAM_ID.*, it should also be able to access the keychain items. Notice that having the keychain access group set to TEAM_ID.* means that only apps developed by same developer (team) can access the RN app's keychain items.

  • you must change the Flutter's library (or the native iOS) source code so that it uses the same keychain service (kSecAttrService) as Expo. For Expo, if You don't pass the keychainService param in the SecureStore options, the kSecAttrService attribute of the item you want to write (or get) will be app. You can see that yourself here, on the expo native library's source code on GitHub (link). I made a fork of the flutter library and I will make a pull request sometime in the future, but you can just change plugin's default keychain service to app as well. It currently defaults to flutter_secure_storage_service, as seen in the plugin's source code on GitHub (link).

  • the trickiest part - because of which I doubted in all of the above steps - is setting the correct key (kSecAttrAccount) for the keychain item I wanted to get. What helped me was to use the Flutter plugin's readAll method (link to the native source code of readAll on GitHub). It showed me that the keychain items were actually there, but that the keys were different than the ones ReactNative app was using. As I've found out by debugging (it's nowhere in the docs!!!), the expo username and expo project's name are added **before the string used for the key**! So, if in the ReactNative app you save a token with key accessToken, it will be saved to the Keychain as @expousername/project-name-accessToken! It's nowhere in the docs, and I don't know how it happens, because I've looked through the source code

In my question, I said that I've copied the Objective-C code from expo's library to Flutter's plugin, but that wasn't necessary.

Share:
1,723
Aleksandar
Author by

Aleksandar

Bachelor in Software engineering, interested in lots of technologies, mostly mobile and IoT. Loves music and blogging, plays piano and saxophone, enjoys good parties, music and humans.

Updated on December 18, 2022

Comments

  • Aleksandar
    Aleksandar over 1 year

    I have a ReactNative app (only published for iOS) which uses ExpoSecureStore to put and read values from iOS Keychain.

    I am transitioning to Flutter and I need to keep the users logged in, i.e. to get their auth tokens from the previously installed RN version of the app.

    So, here is what I do:

    1. I install the RN app from the App store,
    2. Log in (the token is saved with ExpoSecureStore),
    3. Install my Flutter app
    4. Try to access the Keychain - nothing found!
    5. Reinstall the RN app from the App store,
    6. User is logged in when I open the app!

    So, my Flutter installation doesn't delete the tokens, but it can't access them.

    Update 18.3.2020.

    Here is what ReactNative's latest build looks like on AppstoreConnect: React Native entitlements on AppstoreConnect

    Here is the same info for our Flutter build: Flutter entitlements on AppstoreConnect

    I tried setting the keychain group in Flutter's XCode project to all kinds of values: TEAM_ID.*, TEAM_ID., TEAM_ID, *, TEAM_ID.com.my_real_app_id etc.. but nothing helped Entitlements of Flutter app XCode project

    Is there something I am missing?

    ============== original question continues ================

    I am using the FlutterSecureStoragePlugin (link to the .m file) to access the Keychain values.

    Here is the link to the EXSecureStore.m file - EXSecureStore.m on GitHub.

    • The Flutter app has the same bundle ID, so that should not be the problem.

    • I've changed its source code so that I use the same keychain service (kSecAttrService), which defaults to "app".

    • Also, I've copied and adapted the code for reading the Keychain values from ExpoSecureStore.m to FlutterSecureStoragePlugin.m but still no luck..

    Could there be any difference in the way RN and Flutter libraries pass strings down to native iOS? I guess there must be something going on with the Keychain access that I don't know, so any help is very welcome!

    Thank You for reading!