Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android Passkeys need changes to start_passkey_registration challenge #365

Closed
smessmer opened this issue Oct 10, 2023 · 13 comments · Fixed by #371
Closed

Android Passkeys need changes to start_passkey_registration challenge #365

smessmer opened this issue Oct 10, 2023 · 13 comments · Fixed by #371
Labels
enhancement New feature or request

Comments

@smessmer
Copy link
Contributor

smessmer commented Oct 10, 2023

Is your feature request related to a problem? Please describe.
When running the challenge JSON returned by start_passkey_registration against the Credential Manager API on Android, the device asks to set up a security key instead of a passkey. We are using webauthn-rs to create passkeys and are currently manually changing the challenge JSON to conform to what Android needs to set up a passkey:

authenticatorAttachment: "platform",
requireResidentKey: true,
userVerification: "required",
residentKey: "required",

Describe the solution you'd like
Instead of us manually changing the output json from start_passkey_registration, it would be better if it either already had the correct values to create an Android passkey by default, or would allow us to modify these values via an Options input argument to start_passkey_registration.

@smessmer smessmer changed the title Android Passkeys need changes to start_register challenge Android Passkeys need changes to start_passkey_registration challenge Oct 10, 2023
@Firstyear
Copy link
Member

We can't force RK true because that causes security keys to fill up their extremely limited storage. Since we have no way to pre-flag and check what browser or client the user has, we have to set the conservative default that doesn't break devices. And we don't want to add the option to start_passkey_reg for rk=true, because we simply don't want to encourage or allow people to easily set that value since there is so much "anti-pattern" advice floating around.

This is a flaw in android, because it should be behaving the same as iOS - making resident keys opportunistically rather than demanding rk=true be set. It should be showing dialogs that say "create a passkey on my device or use an external security key".

Of course, if you do set rk=true to android, and it pops up the cred manager api then the user may still choose to use a security key! And in that case we've just potentially taken that bit more storage and gotten that user closer to their key not working. In order to prevent this, then we need to send authenticator-attachment=platform to exclude the ability to accidentally muck up a security key. But then we've stopped the user having the choice to use a security key.

So we're pretty much in between a rock and a hard place. Do we trash security keys? Or do we accept that androids experience is flawed and we have to deal with it? Or do we introduce some api's that allow platform only rk=true to be sent and the RP has to pre-check and ID the devices in question and then pop their own UI's to make up for what android and chrome lack?

The whole thing is a hot-mess. There really isn't a good solution here.

We designed the start_passkey api to "do the safe and right thing" on all devices, and I'm not really sure that it's up to us to fix this when Android is what has the problem here.

@smessmer
Copy link
Contributor Author

smessmer commented Oct 10, 2023

I believe with authenticatorAttachment: platform, it should be safe to use rk: required since security keys are forbidden in this case, right? Maybe there is a way to allow configuring the start_passkey api but in a way that forces authenticatorAttachment: platform if rk: required is set?

@Firstyear
Copy link
Member

That would probably be the only way I'd allow this into that api is if it was platform only.

But then it's up to the RP to offer it's own dialogs to say "create a key on my phone" or "create a passkey on a security key".

I'd probably make it a separate API call like "start_platform_only_passkey_registration" or something similar.

@yaleman yaleman added the enhancement New feature or request label Oct 10, 2023
@smessmer
Copy link
Contributor Author

smessmer commented Oct 10, 2023

Ok from my tests with android, the actually important parameter is authenticatorAttachment: platform, which the library currently does not set and allows no way of setting. If authenticatorAttachment is set, then residentKey: required is recommended for Android but doesn't seem necessary. Even with residentKey: discouraged (as currently set by webauthn-rs), Android correctly creates a resident passkey.

So maybe the easiest way is to allow configuration of the authenticatorAttachment parameter only?

@Firstyear
Copy link
Member

Okay, I'm more comfortable with that. I still think it should be start_platform_passkey_registration rather than a parameter?

@smessmer
Copy link
Contributor Author

Either way works for us. But agreed that probably a separate function is better because it might make it possible to later add options for allowing to configure the residentKey parameter. I've only run one test so far and while that seems to show that Android doesn't need it, it's possible that we'll see different behavior on different devices.

@micolous
Copy link
Collaborator

micolous commented Oct 10, 2023

The other issue from memory with the Android+GMS flow is if “Google Passkeys” gets involved, there’s no escape hatch at all, it’s always using a cloud-synchronised credential attached to a Google account. You can’t use a device-bound credential stored in the SE (which GMS will still attempt in some cases), and attachment=platform excludes all hardware security keys.

The experience is also different on non-GMS devices, because GMS provides the FIDO APIs.

By comparison, iOS prompts you to choose between an iCloud synchronised credential, using caBLE or using a security key.

@Firstyear
Copy link
Member

So effectively what you're saying @micolous is that an RP has to make their own UI's for a user to choose between platform/cross-platform, because the device simply lacks the UI to do it natively? This is going to be a nightmare for RP's, not just @smessmer

@smessmer Do you have contacts in Google to push them to change this? We have no contacts into google, and past attempt at public bugreports have fallen flat. Because this will be a problem for other RP's. Android needs to prompt when attachment=null for "make a passkey or attach your security key", rather than one or the other.

@micolous
Copy link
Collaborator

micolous commented Oct 11, 2023

I've gone through and run some tests on Android with Chrome 117 (the version currently offered to my device on Play Store) using the compat_tester, based on "Passkey" mode (which calls start_passkey_registration). I'm using a Pixel 3A (which is outdated), but it appears to have recent GMS and also has BLE and NFC support.

I've flipped flags in Webauthn::start_passkey_registration and WebauthnCore::generate_challenge_register_options in order to see what it does.

rk requireResidentKey authenticatorAttachment uv result
discouraged false None required defaults to USB security key, can choose to use local SE with screen unlock. ⭐
discouraged false Platform required local SE only, prompted for screen unlock immediately
discouraged false CrossPlatform required USB security key only
preferred false None required Google Passkey™ only ❌
preferred true None required Google Passkey™ only ❌
preferred true CrossPlatform required USB security key only
preferred true Platform required Google Passkey™ only
required true None required Google Passkey™ only ❌
required true CrossPlatform required USB security key only
required true Platform required Google Passkey™ only
  • ⭐ - this is compat_tester/webauthn-rs' current default behaviour
  • ❌ - this incorrectly restricts use of cross-platform credentials

At present, it seems like it's impossible on Android to support both Google Passkey™ and another type of authenticator (security key or SE) in a single request – it's entirely up to the RP as to which keys they can use, and if they're even aware of this problem so that they can offer users a choice. Google acknowledge that SEs being broken on Android will be fixed "in the future", but that messaging has been around for some time, and there's no published time frame to fix it.

If there was the option to use Google Passkeys™ with rk=discouraged (like what iOS does with iCloud Keychain), then it'd mean everything should just work with what webauthn-rs does today.

This is an Android/GMS bug – we've attempted to escalate this issue in the past, but public commentary and other actions taken since then suggest that this isn't a priority for them. Google's recommendation is rk=required, attachment=platform, which effectively requires Google Passkeys™ on Android to the exclusion of everything else (even Google Titan Keys). Other recommendations of rk=required, attachment=None have the effect of bricking security keys, which again nobody seems to care about.

I tried using NFC keys as well, that seems broken. When that prompt is up, the phone doesn't beep when touching a security key on the phone. It works in other NFC apps. I tuned a radio scanner to 13.56 MHz with that prompt up and the phone is emitting a constant carrier when there is a key is in the field – just no beep. That makes me think they're using Android's "reader mode", but something's broken. From memory, GMS didn't used to use "reader mode" for security keys, and that all worked fine when this was my primary phone.

@smessmer
Copy link
Contributor Author

Did you use the in-browser flows with Chrome, or the native app experience using Google's Credential Manager? We're using a native app flow but I'm not sure if they're different. For our use case, we're ok with forcing Google Passkeys - on Android 14, they open it up to non-Google password managers, so it's not a fully closed platform and the user does get a choice of where to store them. Authenticators that aren't password managers are nice but not currently a priority for us. Showing a dialog with such a choice, at least in the way the OS dialogs are now, would confuse users who might not even know what passkeys are, and know about other authenticators even less.

@Firstyear
Copy link
Member

This was in-browser. Even if they allow other pwmanagers in their passkey flow, we need to consider all users of this library, not just your use case. And there are a lot of tech users especially with fidokeys that we need to care about too. So we need to worry about our defaults and what that will trigger for our libraries consumers, and our defaults should do "the correct thing". The problem here is Android seems to have no "correct thing" we can just select as a default and proceed with.

Showing a dialog with such a choice, at least in the way the OS dialogs are now, would confuse users who might not even know what passkeys are, and know about other authenticators even less.

Doesn't seem to be an issue on iOS 🤔 ?

But also, yes I have done user testing with this to non-technical people and passkeys do confuse them. But this is inherently a problem with Android's UI's and the whole workflow of passkeys as a whole, not what we do in Webauthn-rs. I'll repeat - Androids UI's are not fit for purpose and we are not in control of that. iOS has no issues with users in testing by comparison.

For example, Androids UI's don't communicate how the screen unlock or the "passkey" actually works or how it's secured, so I saw people exit out and go back to use a password. I saw someone who saw "security key" in the prompt and thought they were hacked. And of course, as we've seen with the github passkey adoption thread, there is an undercurrent of many android devices that simply fail to make passkeys and error - the only recovery step is to reset the device.

So right now, what is the choice we make? Is it even right to recommend you to offer google's Passkeys given the widespread issues they have? Or do we encourage you to use the security key + secure enclave flow at the expense of other password managers? Or do we say "don't support android until their fix their platform"? What's going to be best for the humans that will be indirectly consuming webauthn/passkeys via your deployment - the same humans who have already shown and reported that androids entire passkey flows are flawed?

I think that you are going to have a lot of frustrated users running into issues here no matter what we choose.

I really strongly would urge you to contact google and ask them to fix this behaviour. I don't think it's up to every website to work around Androids defects.

I think if we are to add a "prompt android for passkeys" api call, I think I would feature gate it, and have some big warnings on it since it requires pre-checking the capabilities of the device.

If I am to add it, it's going to be "rk=require, attachment=platform, transports=internal, uv=required". I want to make sure there is no possibility to brick a yubikey by using this api.

But for now I want another day or two to think about it and talk about it with @micolous

@micolous
Copy link
Collaborator

For our use case, we're ok with forcing Google Passkeys - on Android 14, they open it up to non-Google password managers, so it's not a fully closed platform and the user does get a choice of where to store them.

@smessmer Whether it's "open to other password managers" is irrelevant, GMS still broke security keys. If a user wants to use their security key, then it's on the platform and the RP to not limit their choices.

Android 14 is still a long way away for the majority of Android users, and most devices today will never get that update. Pixel and other developer-preferred devices have significantly better update cadence than your average Android phone.

@Firstyear
Copy link
Member

@smessmer Whether it's "open to other password managers" is irrelevant, GMS still broke security keys. If a user wants to use their security key, then it's on the platform and the RP to not limit their choices.

In fact, it's an explicit goal of webauthn wg (and even the google reps) have said "users should always be free to chose any credential they want"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants