mogol / flutter_secure_storage

A Flutter plugin to store data in secure storage
https://pub.dartlang.org/packages/flutter_secure_storage
BSD 3-Clause "New" or "Revised" License
1.09k stars 340 forks source link

Message: The specified item already exists in the keychain, IOS #711

Open yourior opened 1 month ago

yourior commented 1 month ago

[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

KasperTidemann commented 1 month ago

@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.

yourior commented 1 month ago

@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());

viphoamt commented 1 month ago

I also had the same error. Please help me!

Thioby commented 1 month ago

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.

devapalanisamy commented 1 month ago

use readAll to get value from Storage. (await secureStorage.readAll())[StorageConstants.secureStorageKey]; This fixes the issue

PROGrand commented 1 month ago

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.

usa-12 commented 1 month ago

I had also same error faced. Kindly provide the solution ![Uploading Screenshot 2024-05-17 at 12.10.55 PM.png…]()

usa-12 commented 1 month ago

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)

meg4cyberc4t commented 1 month ago

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

MMA0807 commented 1 month ago

is there any promotion?

juliansteenbakker commented 1 month ago

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

kaciula commented 1 month ago

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.

@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.

vsaase commented 1 month ago

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

robinsjovoll commented 3 weeks ago

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.

justprodev commented 3 weeks ago

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.

koji-1009 commented 3 weeks ago

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...

kaciula commented 3 weeks ago

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?

vsaase commented 2 weeks ago

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

koji-1009 commented 2 weeks ago

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.

justprodev commented 2 weeks ago

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);
}
Akhrameev commented 1 week ago

@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.

dballance commented 1 week ago

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.

koji-1009 commented 6 days ago

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.

JgomesAT commented 2 days ago

Any new,?