【Flutter】Keychainアクセスのオプションと、iOSネイティブ連携してる場合のハマりポイント
flutter_secure_storage
前提として、Flutter側はflutter_secure_storageを使用しています。
FlutterにおけるiOSのKeychainアクセスにはこのパッケージが有名だと思います。
パッケージの実装の概要としては、MethodChannel[1]でプラットフォーム側のセキュア情報にアクセスします。
iOSのKeychainアクセスにおけるオプション
flutter_secure_storageではKeychainAccessibilityというオプションが用意されており、端末のアンロック状態やパスコード設定有無によってアクセス制限の堅さ分けがされています。
属性 | 概要 | デバイス制約 |
---|---|---|
passcode | 端末にパスコードが設定されていてロック解除中のみアクセス可能。この中では一番堅い。 | あり(※) |
unlocked | ロック解除中のみアクセス可能。デフォルト。 | なし |
unlocked_this_device | ロック解除中のみアクセス可能。 | あり(※) |
first_unlock | 端末の再起動後に一度ロック解除されるまでアクセスできない。(再起動後のロック解除後はロック画面中でもアクセス可能) | なし |
first_unlock_this_device | 端末の再起動後に一度ロック解除されるまでアクセスできない。(再起動後のロック解除後はロック画面中でもアクセス可能) | あり(※) |
ピュアFlutterの場合はシンプル
ピュアFlutterの場合は、上述のflutter_secure_storageのKeychainAccessibilityを用途によって使い分ければ良いです。
基本的にはデフォルトで足りると思いますが、例えばロック画面でもアクセスしたいならfirst_unlock
を使います。
final options = IOSOptions(accessibility: KeychainAccessibility.first_unlock);
await storage.write(key: key, value: value, iOptions: options);
ネイティブ連携してる場合の注意⚠️
ここからのお話は、デフォルト(つまりunlocked
)の場合は関係ないかもしれません。
次の場合は注意が必要です
- ネイティブ連携していて、かつネイティブiOS側(Swift)とFlutterの両方から同じKeychainの値にアクセスしている
- さらに
first_unlock
などデフォルト以外を使っている場合
- さらに
私はこのパターンに当てはまっていたので、Flutterでfirst_unlock
を設定しているのにロック画面でKeychainからデータ取得できずハマりました😭
原因は、Swift側ではデフォルト(unlocked
)になっていたことです。
For example, by default, you can only access keychain items when the device is unlocked.
When Unlocked
Items with this setting are only accessible when the device is unlocked. A device without a passcode is considered to always be unlocked. This is the default accessibility when you don’t otherwise specify a setting.
結論として、FlutterとSwift側で同じ属性を設定できていれば問題はありません。
first_unlock
などデフォルト以外を使うときは、Swift側にも設定が必要です。
iOS(Swift)側の設定
要はflutter_secure_storageと同じ感じで、kSecAttrAccessibleAfterFirstUnlock
など任意の属性を選ぶだけです!
公式ドキュメントより
私はネイティブiOSのKeychainアクセスにはkishikawakatsumiさんのKeychainAccessを使っています。
Flutterでfirst_unlock
を指定した場合
final options = IOSOptions(accessibility: KeychainAccessibility.first_unlock);
await storage.write(key: key, value: value, iOptions: options);
Swiftでもこのように書けば、両者でアクセスが可能です!
let keychain = Keychain(service: "com.example.github-token")
.accessibility(.afterFirstUnlock)
参考
- https://blog.kishikawakatsumi.com/entry/2015/01/03/082916
- https://qiita.com/sachiko-kame/items/261d42c57207e4b7002a
- https://flutter.tnantoka.com/entry/2019/05/29/225128
- https://zenn.dev/yokojp/articles/0ff8fc44f48857
Discussion