问题
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:
- I install the RN app from the App store,
- Log in (the token is saved with ExpoSecureStore),
- Install my Flutter app
- Try to access the Keychain - nothing found!
- Reinstall the RN app from the App store,
- 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:
Here is the same info for our Flutter build:
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
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
toFlutterSecureStoragePlugin.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!
回答1:
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 isTEAM_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 itemTEAM_ID.*
to thekeychain access groups
array. Notice that if You are adding the first item, there is no.
before the*
, sinceAppIdentifierPrefix
is converted intoTEAM_ID.
before the build. Link to the apple docs on the keychain-access-groups entitlement.
- setting the Keychain Sharing capability to
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 thekeychainService
param in the SecureStore options, thekSecAttrService
attribute of the item you want to write (or get) will beapp
. 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 toapp
as well. It currently defaults toflutter_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'sreadAll
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 keyaccessToken
, 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.
来源:https://stackoverflow.com/questions/60703577/access-keychain-values-stored-by-exposecurestore-from-a-flutter-app-ios