russhwolf / multiplatform-settings

A Kotlin Multiplatform library for saving simple key-value data
Apache License 2.0
1.7k stars 67 forks source link

Update addKeychainItem(...) to improve compatibility with FaceID #193

Closed crysxd closed 6 months ago

crysxd commented 6 months ago

This PR improves the way addKeychainItem(...) distinguishes between adding and updating an item when used with FaceID. The original implementation caused two operations on the key chain:

  1. Check if key exists
  2. Update or add

This caused two consecutive FaceID prompts for the user. The new implementation attempts to add the item but then checks for errSecDuplicateItem. This error occurs before the FaceID prompt is shown. If the operation fails with this specific error we proceed to update the item, causing a FaceID prompt.

To use KeychainSettings with FaceID, you can use this snippet:

fun <T> withBiometricSettings(
      service: String,
      reason: String,
      block: KeychainSettings.() -> T
): T {
    val context = LAContext().apply {
        localizedReason = reason
    }

    val settings = KeychainSettings(
        kSecAttrService to CFBridgingRetain(service),
        kSecUseAuthenticationContext to interpretCPointer(context.objcPtr()),
        kSecAttrAccessControl to SecAccessControlCreateWithFlags(
            kCFAllocatorDefault,
            kSecAttrAccessibleWhenUnlockedThisDeviceOnly as CFTypeRef,
            1UL shl 3, // biometryCurrentSet
            null
        ),
    )

    return block(settings)
}

withBiometricSettings(reason = "Login") {
   getStringOrNull(key = "secret")
}
russhwolf commented 6 months ago

Thanks. I'm going to tweak this a little but the general idea makes sense.

crysxd commented 6 months ago

Amazing! Thank you! Any rough ETA for the 1.2 release? I now copied the class into our source code with my modifications.

I use your library in all my projects btw, it's a real life saver 👍