flutter_secure_storage
flutter_secure_storage copied to clipboard
Message: The specified item already exists in the keychain, IOS
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(Unexpected security result code, Code: -25299, Message: The specified item already exists in the keychain., -25299, null) #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:651:7) #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:334:18)
#2 WriteStringStorage (package:pkgname/Storage/Secure_Storage.dart:34:10) #3 GoRouterSplashScreen.build. . (package:pkgname/Splash_Screen.dart:39:13) #4 GoRouterSplashScreen.build. (package:pkgname/Splash_Screen.dart:36:9) #5 _FlutterSplashScreenState.didChangeDependencies (package:another_flutter_splash_screen/another_flutter_splash_screen.dart:325:7) Type: Notice | Timestamp: 2024-05-11 09:26:17.155593+07:00 | Process: Runner | Library: Flutter | TID: 0x12e46f
here is my initialize code
AndroidOptions _getAndroidOptions() => const AndroidOptions(
encryptedSharedPreferences: true,
);
IOSOptions _getIOSOptions() => const IOSOptions(
// synchronizable: true,
accessibility: KeychainAccessibility.first_unlock
);
final storage = FlutterSecureStorage(
aOptions: _getAndroidOptions(),
iOptions: _getIOSOptions()
);
the error showed when i want to write a value into a key
storage.write(key: Keyname,value: value);
version: "9.1.1"
is this a bug or is there a setting that i need to set for IOS? because this works in android
@yourior I’m seeing the same error here, not sure what it’s related to just yet. It used to work just fine, so I’m currently investigating.
@KasperTidemann it works by initializing it using the default method
final storage = const FlutterSecureStorage();
then call the option using the function
storage.write(key: Keyname,value: value,iOptions: _getIOSOptions(),aOptions: _getAndroidOptions());
I also had the same error. Please help me!
This is beacuse of that changes: https://github.com/mogol/flutter_secure_storage/commit/1364ad9937bcd7834142aff967fc40d288ea8ea8
This is breaking change. Should be WRITTEN DOWN IN CHANGELOG. From this commit, accessibility now is part of query path to keychain.
use readAll to get value from Storage. (await secureStorage.readAll())[StorageConstants.secureStorageKey]; This fixes the issue
None of previous suggestion does works.
Seems, that some keychain access keys or ciphers or appleid-related stuff is changed in latest ios update. So keychain data, written previously by this or another application with the same attributes became unaccessible. Solvable by adding custom attribute (accountName for example):
iosOptions.accountName='something-different-from-previous'
Options can be passed in constructor. No need for write, read, delete special options. In case of further ios keychain critical updates accountName should be changed again.
I had also same error faced. Kindly provide the solution
I have faced this error. Yesterday work fine but today has error show.
PlatformException(Unexpected security result code, Code: -25300, Message: The specified item could not be found in the keychain., -25300, null), stackTrace: #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:648:7)
#1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:334:18)
Hi @bierbaumtim.
Your code here has a breaking change, which is not described in changelogs and it is unclear how to get around it or fix the code. Please give a couple of comments, because right now the only option is to go back to the version is set in pubspec.yaml flutter_secure_storage: 9.0.0
is there any promotion?
I have released v9.2.1 which should fix this, however there are atill some reports that i is not fully functional just yet, so i'm going to investigate further
None of previous suggestion does works.
Seems, that some keychain access keys or ciphers or appleid-related stuff is changed in latest ios update. So keychain data, written previously by this or another application with the same attributes became unaccessible. Solvable by adding custom attribute (
accountNamefor example):iosOptions.accountName='something-different-from-previous'Options can be passed in constructor. No need for
write,read,deletespecial options. In case of further ios keychain critical updatesaccountNameshould be changed again.
@PROGrand Do you have a link to more info about the iOS keychain breaking changes? And what iOS versions are affected?
Some of my users are being automaticallty logged out because the keychain data is no longer accessible. If I use a custom accountName for the ios options, will I be able to access the old keychain data? Or does this change apply only from now on when writing new data?
When you say "written previously by this or another application", do you mean that in the case of an iOS device that has 2 random apps installed that use the flutter_secure_storage with the default ios options, one of the app's use of the plugin makes the other's app's data unaccessible? Or am I understanding this wrong? By default, the accountName used by flutter_secure_storage is identical in all apps that use it.
still getting this error on write with v9.2.2:
PlatformException (PlatformException(Unexpected security result code, Code: -25299, Message: Das angegebene Objekt befindet sich bereits im Schlüsselbund., -25299, null))
translation of this german string is "The specified item already exists in the keychain"
changing the IOSOptions accountName to something unique did not help
this seems only to happen when the same key is written quickly multiple times, I do not see the error when setting a breakpoint at the write call
Any updates to this? Upgrading from 9.0.0 to 9.2.2 produces this error and yes, I can add a new accountName, but that basically resets all the saved key/values and every user in our app gets logged out.
That because key was saved using different options.
In my case, I started using first_unlock, but keys were saved without it and I use read/delete(key, iOptions: IOSOptions()) then saves with new options.
https://github.com/mogol/flutter_secure_storage/compare/v9.2.1...v9.2.2
Starting with v9.2.2, kSecAttrAccessible has been changed to a non-nil value when reading/writing. So (for example) a value written to KeyChain with accessibility as nil in v9.0.0, will be read in v9.2.2 with accessibility as kSecAttrAccessibleWhenUnlocked.
If accessibility is used to check for SecItemCopyMatching, would it not be necessary to check both the nil case and the accessibility case with the write function?
https://github.com/mogol/flutter_secure_storage/blob/v9.2.2/flutter_secure_storage/ios/Classes/FlutterSecureStorage.swift#L154-L160
I have confirmed that KeychainAccessibility.unlocked is set as the default for IOSOptions. It's not nil when saving...
I'm trying to find a way to fix this issue but without having to force all my users to sign out. Adding IOSOptions with an accountName seems to solve the original issue but it can no longer access the existing values so the users are forced to sign out. Is it possible somehow to be able to read the existing values if I change iOSOptions?
Did you also notice that setting a breakpoint, similar to introducing a delay between write calls, does not trigger the exception? I only see it for multiple quick calls to write
Could we consider changing the containsKey implementation back from SecItemCopyMatching to a determination by the read method?
If the error code is correct, the write method uses SecItemAdd in the case where SecItemUpdate is used. The difference between containsKey and read is “whether it uses the second argument of SecItemCopyMatching” and read appears to be better suited for this determination.
I'll just leave this here
it works...
const FlutterSecureStorage secureStorage = FlutterSecureStorage(
iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
);
String? secureKey;
try {
await secureStorage.waitingAvailability();
secureKey = await secureStorage.read(key: _secureKeyName);
// try to read with old options
if (secureKey == null && Platform.isIOS) {
secureKey = await secureStorage.read(key: _secureKeyName, iOptions: const IOSOptions());
// rewrite with new options
if (secureKey != null) {
Logger(loggerSecurity).info('Rewrite secure key with new options');
await secureStorage.delete(key: _secureKeyName, iOptions: const IOSOptions());
await secureStorage.write(key: _secureKeyName, value: secureKey);
}
}
} catch (e, t) {
Logger(loggerSecurity).severe('Failed to read secure key from storage', e, t);
}
@justprodev Thank you for your solution. I failed to find secureStorage.waitingAvailability(); method and initiated the same migration just after initialization (if data is already available) step for all keys iterating one by one with similar code. And call the same migration process as a reaction for onCupertinoProtectedDataAvailabilityChanged listener.
This is beacuse of that changes: 1364ad9
This is breaking change. Should be WRITTEN DOWN IN CHANGELOG. From this commit, accessibility now is part of query path to keychain.
This is the only part of this discussion that is important and should be heeded by the authors / publishers of FlutterSecureStorage.
The change to Accessibility in this commit now DISALLOWS any calls to READ without an Accessibility set. This flag is largely unnecessary on read as it potentially results in a narrow scope in the search of the keychain for a stored key.
Furthermore, you cannot return to the previous query. It is no longer possible to read without passing the accessibility flag. This thread is a result of this breakage - you cannot read in the same manner as before.
AppleOptions.accessibility should be made nullable for some queries, even if it's required for writes.
https://github.com/mogol/flutter_secure_storage/blob/v9.0.0/flutter_secure_storage/ios/Classes/FlutterSecureStorage.swift#L125-L138
The values written to Keychain using flutter_secure_storage have had kSecAttrAccessible set at write since v6.1.0 (from the version rewritten to swift as far as we can tell).
I am not familiar with objective-c, so I can't say that there is nothing wrong with the code before that.
What I am wondering is if there are "values stored with accessibility of nil". As far as I can see, that problem does not appear to occur in the values stored by flutter_secure_storage.
Also, I think https://github.com/mogol/flutter_secure_storage/pull/719 has more information on the problem that occurs when Accessibility is set to nil on read.
Any new,?
Any new, I think this erro is critical for users with iOS devices and i have a lot of errors in crashlytics with this problem
Any new, I think this erro is critical for users with iOS devices and i have a lot of errors in crashlytics with this problem
Me too
Issue comes from iOS accessibility.
In my case, I had the user that was using app with old flutter_secure_storage and default storage initialization:
static const _storage = FlutterSecureStorage();
In new app version, I use flutter_secure_storage: ^9.2.2 and such kind of initialization:
static const _storage = FlutterSecureStorage(
/// We need this accessibility, as we have api calls that can proceed when app is locked
iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
);
When my old user installed new app version he faced following issues:
- _storage.read(key: _accessTokenKey) -> returns null for key that exists
- _storage.write(key: _accessTokenKey, value: token) -> returns error "The specified item already exists in the keychain."
- _storage.delete(key: _accessTokenKey) -> does't solve issue, as it does't delete entry, resulting in point 2 to repeat again.
In order to fix above issue and not to log out another users I have implemented the fix that works perfectly:
static Future<void> setAccessToken(String? token) async {
try {
await _storage.write(key: _accessTokenKey, value: token);
} catch (e) {
Log.E('FlutterSecureStorage setAccessToken error - $e');
await _saveToStorageErrorCallback(key: _accessTokenKey, value: token); // this line of code was added
}
}
static Future<void> _saveToStorageErrorCallback({
required String key,
required String? value,
}) async {
/// Try to DELETE with default option and write again with new ones
try {
await _storage.delete(key: key, iOptions: const IOSOptions());
await _storage.write(key: key, value: value);
} catch (e, trace) {
Log.E('_saveToStorageErrorCallback: $e\n\n$trace');
}
}
In version 9.0.0, the function delete('session') internally calls the following Swift code:
var keychainQuery: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: 'session'
]
SecItemDelete(keychainQuery as CFDictionary)
Since version 9.2.2, kSecAttrAccessible is always present. You have set the value to KeychainAccessibility.first_unlock:
var keychainQuery: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock,
kSecAttrAccount: 'session'
]
SecItemDelete(keychainQuery as CFDictionary)
The problem arises because the default value for kSecAttrAccessible in version 9.0.0 is kSecAttrAccessibleWhenUnlocked. By adding the kSecAttrAccessible parameter in version 9.2.2, the key cannot be deleted correctly if it was stored with a different kSecAttrAccessible value.
In my opinion, the kSecAttrAccessible parameter should not be specified when deleting, as we always want to delete the key regardless of the kSecAttrAccessible attribute it was stored with, with the exception of kSecAttrAccessGroup and kSecAttrService.
Further Changes
The issue with kSecAttrAccessible also affects reading and writing operations. A comprehensive solution would involve using kSecAttrAccessible only when storing (writing) and not considering it for delete or read operations.
A possible approach that implements this can be found in the following Pull Request: Mogol Flutter Secure Storage PR #751
This PR includes the necessary adjustments to correct the described behavior.
We have hit the same issue. @frankteller-de does your PR fix the issue for users who have data saved befre version 9.x?
We have hit the same issue. @frankteller-de does your PR fix the issue for users who have data saved befre version 9.x?
What I see in Crashalytics, it works at the moment.