webauthn
webauthn copied to clipboard
Conditional creation incompatible with `uvInitialized` semantics in Chapter 7?
https://w3c.github.io/webauthn/#sctn-createCredential says
The client MUST set BOTH requireUserPresence and requireUserVerification to FALSE when options.mediation is set to conditional unless they may explicitly performed during the ceremony.
However then that means that uvInitialized is set to FALSE in the credential record according to https://w3c.github.io/webauthn/#reg-ceremony-create-credential-record
which means that the credential created may not be used for authentication
When this is false, including an authentication ceremony where it would be updated to true, the UV flag MUST NOT be relied upon as an authentication factor.
https://w3c.github.io/webauthn/#abstract-opdef-credential-record-uvinitialized
This feels like it is in contradiction with each-other. The whole idea of conditional creation is that we automatically create a passkey for subsequent log ins. However this is incompatible with the uvInitialized semantics from my reading?
which means that the credential created may not be used for authentication
When this is false, including an authentication ceremony where it would be updated to true, the UV flag MUST NOT be relied upon as an authentication factor.
That is not what this text was intended to mean. The credential certainly can be used for authentication as a possession factor. The thing here is that much like a WebAuthn credential as a whole is "trust on first use" (TOFU), the UV flag is also a TOFU attribute on its own.
The first time the RP sees UV=1 for a given credential, that is not (from that RP's point of view) a guarantee of a successful second authentication factor, because that second authentication factor had not yet been established with this RP. The first time the RP sees UV=1 for a credential, all that says is that some user has set up some UV method on that authenticator.
For example: Alice has a CTAP2 security key without a PIN set. She registers this security key on example.org still without a PIN set, and then leaves the security key unattended on her desk. Bob takes Alice's security key and sets a PIN on it. He then uses it to authenticate as Alice to example.org, which sets userVerification: "preferred". Bob knows the PIN, so the UV succeeds. This is the first time example.org sees UV=1 with this credential, and this UV=1 clearly does not mean that it was Alice that performed this UV. Contrast this with if Alice had set a PIN and authenticated (or registered) with UV before Bob got hold of the security key: then the RP would be assured that whenever it sees UV=1 for the second or later time, it's Alice that performed that UV (unless Alice has shared the PIN with someone, thereby authorizing them to operate her account on her behalf).
Only after seeing the first registration/assertion with UV=1 can the RP be assured that the second and subsequent times it was the same user (or one authorized by the original user) that performed UV during that assertion ceremony.
So I don't think there's any contradiction or incompatibility here, but evidently it is unclear. I think it would be appropriate to at least mention this in step 17 of §7.2. Verifying an Authentication Assertion:
- Determine whether user verification is required for this assertion. User verification SHOULD be required if, and only if, pkOptions.userVerification is set to required. If user verification was determined to be required, verify that the UV bit of the flags in authData is set. Otherwise, ignore the value of the UV flag.
Do you have any other suggestions on how we could make this clearer?
Sorry I didn't make myself completely clear. I understand it can still be used as a posession factor. What I mean is that it can not be used as the Sole authentication signal.
Do I then understand correctly that a passkey added through conditional create will needs to be used at least one more time in combination with a password after registration before we can use it as the sole factor for sign in?
That seems to be in contradiction with the flow the explainer originally tried to describe. Namely automatically create a passkey during password login so that next time you dont need to enter a password. But at this point we don't know anything about the passkey's capability of UV. So on next login someone uses the passkey and has UV set to 1. UVinitinalized is still false so we need to ask for a second authentication factor (the users password) and only then can we set UVinitinalized to true.
That means that with the conditional create flow a person needs to enter their password twice?
Once before the key is automatically created. And another time on subsequent login where we haven't proven yet that the key is suitable to be used as the sole method.
Therefore, updating uvInitialized from false to true SHOULD require authorization by an additional authentication factor equivalent to WebAuthn user verification.
Which isn't as smooth as it was originally intended I think?
Like. This flow would only be smooth if I set uvInitialized = true immediately during registration regardless of the fact that registration returned UV = false but this seems like a security hole. As it allows for the scenario you describe where Bob can set a PIN on Alice's key
I just don't understand how the UX of conditional create as described in the explainer and in for example https://developer.chrome.com/docs/identity/webauthn-conditional-create should work with these constraints in mind
The chrome blog post only says the following:
The registration response returns both "User Presence" and "User Verified" as false, so the server should ignore these flags during credential verification.
Which doesn't really help . What does ignore mean? That I just set uvInitialized to true unconditionally ? But how do I then avoid Bob taking Alice's key and setting a PIN that Alice doesn't know and then sign in to Alice's account?
Good points! @pascoej any thoughts on this?
I think it could be reasonable for conditional registration to return UV=1 if the client checked some authentication factor "equivalent to WebAuthn user verification" while it "recently mediated an authentication". For example: if a password manager was used to autofill a password and then do the conditional registration, and the passphrase/key/biometric/etc for the password vault is the same that the password manager would use for passkey UV, then I think you can argue those are "equivalent" and you could reasonably set UV=1 in the conditional registration. I have not thought about how something like that could be expressed in spec language, though.
I think the idea with UV=0 is that we shouldn't re-use the recent authentication and RPs using conditional create should have some special handling for these upgrade registrations to handle UV=0. This also aligned nicely with where I recall we landed with regard to #2034 and #2021.
@ve7jtb made an acute observation here: the primary use case for conditional registration is to seamlessly upgrade users that currently have just a password as the sole authentication factor. In that case, since the transition is authorized based on a single-factor authentication, then the resulting WebAuthn credential will also implicitly be equivalent to a single authentication factor, even if it happens to have the UV flag set. So the description of uvInitialized:
When this is
false, including an authentication ceremony where it would be updated totrue, the UV flag MUST NOT be relied upon as an authentication factor.
doesn't contradict this use case if the RP isn't relying on UV as an authentication factor. Only if it wants to begin relying on UV as a second authentication factor, then it SHOULD base the authorization of that transition on some other equivalent second factor if one is available for the user account. If the account has only a single authentication factor set up (e.g., only a password, or only one WebAuthn credential), then of course you can't do that, in which case it's fine to trust the initial observation of UV=1 as the initialization of a second authentication factor for the account. Just like if you have only a password set, then the initial setup of an OTP is also trusted-on-first-use as the initialization of 2FA for the account.
So from that perspective there's no contradiction, since you start with single-factor and end with a different single-factor, and the 2FA recommendation in the uvInitialized description only applies when you want to go from single-factor to two-factor.
Does that make sense?
The counterargument to this would be in the case that the user account is already set up with 2FA - for example a password and OTP - and both were used just before a conditional registration was made. In that case yes, you'd get UV=0 in that conditional registration and the first UV=1 assertion with that credential SHOULD still be considered single-factor until authorized as 2FA by one of the other authentication factors. This is unfortunate but likely to be a fairly rare situation. We'll leave conditional registration with UV=0 for L3, but may consider adjusting to allow conditional registration with UV=1 for L4.
Though what you write makes sense I don’t agree with
primary use case for conditional registration is to seamlessly upgrade users that currently have just a password as the sole authentication factor.
I don’t think that’s RP’s expectations. Most people are looking at passkeys to replace password+TOTP. And probably already have thousands of users they want to switch to passkeys that are already set up with TOTP. So we need to explain things in that context.
So then conditional create not allowing without extra hoops . that is surprising and should be clearly explained in the spec to avoid misimplementation.
Google’s developer docs definitely don’t explain any of these nuances and don’t meet the bar at giving people the guidance needed to build a secure implementation.
This is tangentially related, but what is the required behavior from clients if mediation is "conditional", but userVerification is "required"? Does one override the other? The quoted section states:
The client MUST set BOTH requireUserPresence and requireUserVerification to FALSE when
options.mediationis set toconditionalunless they may explicitly performed during the ceremony.
One interpretation of that section suggests that userVerification is overridden since clients "MUST" set requireUserVerification to false. Another interpretation is that userVerification of "required" is retained since the section provides an out with "unless they may explicitly [be] performed during the ceremony".
The ceremony validation criteria only states that user presence is allowed to be false when mediation is "conditional", but it doesn't state that user verification is allowed to be false when "required" was requested which further suggests that userVerifcation "wins" since the ceremony is almost guaranteed to fail.
Am I correct that if mediation is "conditional" and userVerification is "required", the client MUST set requireUserVerification to true?
No, that would throw an error: https://w3c.github.io/webauthn/#effective-user-verification-requirement-for-credential-creation
Let userVerification be the effective user verification requirement for credential creation, a Boolean value, as follows. If
pkOptions.authenticatorSelection.userVerification
is set to
required
- If
options.mediationis set toconditionaland user verification cannot be collected during the ceremony, throw aConstraintErrorDOMException. [...]
Ah, missed that. So either an error will happen or user verification will be required. What’s an example of where user verification can be collected during the ceremony with conditional mediation?
Well Safari + Passwords app on my iPhone 15 Pro Max running iOS 18.5 does not error when I require user verification with conditional mediation; instead the app incorrectly creates the credential despite not enforcing user verification causing the ceremony to fail on the server side (which correctly enforces the UV bit).
I suppose this is a bug in Safari/Passwords though which wouldn't be the first. I know Safari and Passwords incorrectly creates a credential using ES256 even if ES256 is not one of the accepted algorithms despite the spec mandating that an error be reported if none of the COSEAlgorithmIdentifiers are supported.
The chrome blog post only says the following:
The registration response returns both "User Presence" and "User Verified" as false, so the server should ignore these flags during credential verification.
That blog post is not entirely correct. For example, when I use Google Password Manager + Chrome on a Samsung Galaxy S24 running Android 15, user verification is enforced and the UV bit is 1 even if the RP passes "discouraged" for user verification; furthermore the server is not allowed to "ignore" the UV bit per the registration ceremony criteria:
- If the Relying Party requires user verification for this registration, verify that the UV bit of the
flagsin authData is set.
In contrast, the server is allowed (and in fact required) to ignore the UP bit when conditional mediation is used:
- If options.
mediationis not set toconditional, verify that the UP bit of theflagsin authData is set.
Unfortunately as stated above, the way Safari + Passwords on iPhones implement conditional mediation is wrong since it neither errors nor enforces user verification (i.e., the UV bit is 0) when user verification is required. I raised a bug in hopes that is addressed.