Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[local_auth] Fix getEnrolledBiometrics returning non-enrolled biometrics on Android. #5309

Conversation

BeMacized
Copy link
Contributor

@BeMacized BeMacized commented Apr 20, 2022

This PR fixes the issue where the Android implementation of getEnrolledBiometrics returned biometrics that have present hardware on the device, but are not enrolled. This brings it in line with the behaviour on iOS.

Relevant issue:

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the relevant style guides and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/plugins repo does use dart format.)
  • I signed the CLA.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I listed at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.
  • I updated CHANGELOG.md to add a description of the change, following repository CHANGELOG style.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Copy link
Contributor

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good, but this seems like a good opportunity to do some more cleanup/simplification around the weird method channel API we have here.

if (packageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)) {
biometrics.add("iris");
}
// If no biometrics are enrolled, only "undefined" is returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a bizarre implementation anyway, and you're already adjusting the method channel slightly, let's just split out a separate method channel call that's backed by canAuthenticateWithBiometrics. Then this method can only return enrolled biometrics, eliminating "undefined", and the Dart code that currently relies on this special value can instead use that new method channel call directly.

Copy link
Contributor Author

@BeMacized BeMacized Apr 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the current implementation, the undefined value gets ignored on the dart side, both for Android and iOS. As such, I don't believe there is any dart logic that is currently depending on this value, it's just a weird detail being sent back by the native iOS implementation.

Should we just consider removing undefined alltogether in both implementations instead?

Additionally, we could add a second method to the interface (e.g. getPresentBiometrics) that just returns a list of all present biometric types, disregarding wether they are enrolled or not, so that this functionality remains available on Android. (But this would then need to also be implemented on iOS)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the current implementation, the undefined value gets ignored on the dart side, both for Android and iOS. As such, I don't believe there is any dart logic that is currently depending on this value, it's just a weird detail being sent back by the native iOS implementation.

It's not ignored, it's how this works.

Should we just consider removing undefined alltogether in both implementations instead?

Yes, I'm suggesting that we remove "undefined" in favor of a new platform channel method that allows direct querying of whether the device supports biometrics. I.e., have a deviceSupportsBiometrics method call, and replace the code I linked to above with a call to that.

(I'd like to do it for iOS at some point, but for this PR we should just do Android).

Additionally, we could add a second method to the interface (e.g. getPresentBiometrics) that just returns a list of all present biometric types, disregarding wether they are enrolled or not, so that this functionality remains available on Android. (But this would then need to also be implemented on iOS)

I'm not sure what you mean by "this functionality"; the functionality that we need to preserve is just deviceSupportsBiometrics working.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not ignored, it's how this works.

Am I missing something? here the value seems to be ignored (same on iOS), which results in an empty list regardless of whether the undefined value was returned by the native side, which is what deviceSupportsBiometrics uses there.

the functionality that we need to preserve is just deviceSupportsBiometrics working.

Right, which needs to return true even if those biometrics are not enrolled, which would then mean deviceSupportsBiometrics is broken on iOS as well, as undefined is ignored there too.

conclusion being that deviceSupportsBiometrics needs its own method channel call on both platforms, correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I missing something? here the value seems to be ignored (same on iOS)

🤦🏻

Even after all the discussion about this when you initially extracted the default method channel implementation, I still missed that in review. That's a regression from the refactor: deviceSupportsBiometrics is not supposed to be calling the Dart function, it's supposed to be directly invoking the method channel call:

dd2cd96#diff-41eeb8757de140d2fbb1c3650b056c1721926ef417103bf2084a3cc48d1f504fR126-R129

Right, which needs to return true even if those biometrics are not enrolled, which would then mean deviceSupportsBiometrics is broken on iOS as well

Yes, the regression broke iOS too. And also any third-party implementations that relied on this behavior (likely that's none, but it's still wrong). We need to fix this in all three packages.

For the default method channel we need to restore calling the method channel directly. For iOS and Android, we should add a new method. If we didn't already know how confusing the undefined implementation was, the fact that I was specifically watching for it in the refactor and still missed that the final version wasn't right certainly confirms it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can put together a PR for the default method channel now, and this PR can be updated to fix Android. Can you do a PR after this one to mirror what you add here to the iOS implementation to fix that regression?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, can do. I'll fix this for Android now, and I'll try submit a fix for the iOS implementation tomorrow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stuartmorgan-g added a commit to stuartmorgan-g/plugins that referenced this pull request Apr 21, 2022
Fixes a regression from the federation of local_auth in the default
method channel implementation of `deviceSupportsBiometrics`. It needs to
use the unfiltered method channel list in order to work correctly, not
the results of the Dart method that filterns out the sentinel for
unenrolled devices.

See flutter#5309 (comment)
for context.
stuartmorgan-g added a commit to stuartmorgan-g/plugins that referenced this pull request Apr 21, 2022
Fixes a regression from the federation of local_auth in the default
method channel implementation of `deviceSupportsBiometrics`. It needs to
use the unfiltered method channel list in order to work correctly, not
the results of the Dart method that filterns out the sentinel for
unenrolled devices.

See flutter#5309 (comment)
for context.
Copy link
Contributor

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Just a few small things.

Copy link
Contributor

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with nit. Thanks for the major improvement to the understandability of this code!

@BeMacized BeMacized added the waiting for tree to go green (Use "autosubmit") This PR is approved and tested, but waiting for the tree to be green to land. label Apr 22, 2022
@RafaRuiz
Copy link

Shouldn't the README be changed?

@stuartmorgan-g
Copy link
Contributor

@RafaRuiz This PR has already been submitted, so cannot be changed; if there are other changes to make please file an issue.

@RafaRuiz
Copy link

I've sent a PR indeed

@stuartmorgan-g stuartmorgan-g mentioned this pull request Apr 28, 2022
11 tasks
mauricioluz pushed a commit to mauricioluz/plugins that referenced this pull request Jan 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
p: local_auth platform-android waiting for tree to go green (Use "autosubmit") This PR is approved and tested, but waiting for the tree to be green to land.
Projects
None yet
4 participants