Skip to content

Conversation

@MasterKale
Copy link
Contributor

@MasterKale MasterKale commented Jul 11, 2024

This PR proposes new error codes to be raised across various WebAuthn interactions. There is an assumption that the user has meaningfully interacted with some part of the ceremony to consent to informing RP's why the ceremony failed.

Fixes #2096.

An explainer is available here: https://github.com/w3c/webauthn/wiki/Explainer:-New-Error-Codes-(2024-Edition)

Note: This PR is targeting #2047 and should not be merged until that PR has been merged.

  • OptOutError
  • UserVerificationError
  • TimeoutError

Preview | Diff

Copy link
Member

@nsatragno nsatragno left a comment

Choose a reason for hiding this comment

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

Some general thoughts:

  • I am in favour of adding more specificity for error states after the user has chosen to go ahead with the authentication ceremony. This would not result in a significant impact to the privacy properties of WebAuthn and if it solves a pain point for RPs, we should do it. Essentially, most circumstances where the user indicates their consent to sign in, an error is shown, and then the user taps "Okay" or "Cancel" could result in more specific errors.
  • However, it's not clear to me that cancelling out of a ceremony constitutes a form of user consent. This PR will allow RPs to distinguish between "the user's authenticator has no passkeys or the authenticator decided the user denied consent" (NotAllowedError) and "the user cancelled out of the ceremony (likely because they did have passkeys but did not consent)" (UserCancelledError). This is especially tricky because the actual browser implementation differs from the letter of the standard with respect to dispatching requests to authenticators, e.g. Chrome will default to the platform authenticator if it knows there's a credential there.

index.bs Outdated
: If |lifetimeTimer| expires,
:: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator|
and [=set/remove=] |authenticator| from |issuedRequests|.
and [=set/remove=] |authenticator| from |issuedRequests|. Throw a "[=create/TimeoutError=]" {{DOMException}}.
Copy link
Member

Choose a reason for hiding this comment

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

General feedback: this PR needs to update section 14, in particular 14.5.2

For example, one such information leak is if the client returns a failure response as soon as the user denies consent to proceed with an authentication ceremony . In this case the Relying Party could detect that the ceremony was canceled by the user and not the timeout, and thus conclude that at least one of the credentials listed in the allowCredentials parameter is available to the user.

This PR is making it possible to distinguish between these cases.

Copy link
Member

@emlun emlun left a comment

Choose a reason for hiding this comment

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

Before getting into specifics: I noted in #2096 (comment) that we can't simply use any value as the name of a DOMException:

When creating or throwing a DOMException, specifications must use one of these names. If a specification author believes none of these names are a good fit for their case, they must file an issue to discuss adding a new name to the shared namespace [...]

So we would need to upstream these new name definitions, or use DOMException derived interfaces instead.

Copy link
Contributor

@selfissued selfissued left a comment

Choose a reason for hiding this comment

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

I support adding these error codes after the cleanups suggested by other commenters.

Base automatically changed from 1859-differentiate-errors to main August 7, 2024 19:50
@MasterKale
Copy link
Contributor Author

I'm inclined to break this PR up to try and get some of the less contentious new error codes into L3 before the upcoming deadline (which those are, I'll try and identify before today's WG meeting.)

@MasterKale
Copy link
Contributor Author

I've taken HybridPrerequisitesError and UserHybridCancellationError out of the running for now for sake of pursuing incremental improvement in WebAuthn's error messages instead.

Comment on lines +1970 to +1974
If the user agent is informing the user that
the last used |authenticator| cannot collect [=user verification=] when
<code>|pkOptions|.{{PublicKeyCredentialCreationOptions/authenticatorSelection}}.{{AuthenticatorSelectionCriteria/userVerification}}</code>
is set to {{UserVerificationRequirement/required}},
throw a "{{UserVerificationError}}" {{DOMException}}.
Copy link
Member

Choose a reason for hiding this comment

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

The equivalent error on the authenticator layer is error code equivalent to "ConstraintError", so I think that could be used here too?

This would lump this together with another ConstraintError thrown when authenticatorSelection.userVerification == "required" and mediation == "conditional", but that is also an error expressing that UV was required but couldn't be performed, so I think that can be okay?

Then we could also add a case to pass through error code equivalent to "ConstraintError" from the authenticator layer, like we do with InvalidStateError.

Copy link
Contributor Author

@MasterKale MasterKale Nov 10, 2025

Choose a reason for hiding this comment

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

Coming back to this comment, ConstraintError is currently used to communicate a couple of issues during registration:

  • A resident key was required but the authenticator doesn't support it
  • UV was required but the authenticator couldn't collect it

I'm kinda bummed out by the prospect of mashing "no rkey support" and "can't do UV" into the same bucket, even though I have to admit it'd make sense to make the change you suggest and turn this proposed UserVerificationError into a ConstraintError because an existing similar error state already sets precedent.

We're probably well into the time where lack of rkey support is the minority cause of a ConstraintError; put another way, my concern may not hold as much water because the current state of passkeys will have RPs implying that a ConstraintError is more likely "can't do UV" than "can't create an rkey."

I'd like to discuss this at the next WG meeting before I make any changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Open question: can we define specific values for a DOMException's message when we define an error to throw? For example, could we define e.g. "throw a ConstraintError with message set to "UV required but authenticator did not support UV"" to throw RP's a bone here if we standardized on using an existing error?

Copy link
Member

Choose a reason for hiding this comment

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

Hm... it looks like we can!

WebIDL §2.8 Exceptions reads:

An example of including additional information used to construct the exception message would be:

Throw a "SyntaxError" DOMException indicating that the given value had disallowed trailing spaces.

This refers to one of the steps in §3.14.3. Creating and throwing exceptions which is linked just afterwards:

To create a DOMException given a string name:
[...]
4. Set ex’s message to an implementation-defined message appropriate for the exceptional situation. The calling specification may contain information to to help implementations construct this message.
Implementations need to be cautious not to leak sensitive or secured information when constructing this message, e.g., by including the URL of a cross-origin frame, or information which could identify the user.

2.8.1. Base DOMException error names does state that "When creating or throwing a DOMException, specifications must use one of these names [listed in the DOMException names table].", but I can't see anything like that for message.

So yes, I think it seems like "The calling specification [WebAuthn] may contain information to to help implementations construct [ex's message]".

@nadalin nadalin added the @Risk Items that are at risk for L3 label Sep 11, 2024
@nadalin nadalin modified the milestones: L3-WD-02, Futures (catch-all) Sep 12, 2024
@varunbgit
Copy link

@agl @pascoej @jschanck
@MasterKale
What is the plan to merge this PR and release it to public?

@mitchgl
Copy link

mitchgl commented Jun 3, 2025

Are there any updates on this? Seems useful for context for RP's

@MasterKale
Copy link
Contributor Author

This work is on the back burner for now while we continue to work on getting L3 through the process to get it to Recommendation so that it officially supplants L2. I have every intention of picking this back up when we're further along this process.

@timothymiko
Copy link

@MasterKale are there any updates on this? Very interested in this as a RP to get more signal on improving the UX of our passkey flows.

the last used |authenticator| cannot collect [=user verification=] when
<code>|pkOptions|.{{PublicKeyCredentialCreationOptions/authenticatorSelection}}.{{AuthenticatorSelectionCriteria/userVerification}}</code>
is set to {{UserVerificationRequirement/required}},
throw a "{{UserVerificationError}}" {{DOMException}}.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we have to replace the UserVerificationError during registration with ConstraintError, then we'll have to replace this one during authentication as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

@Risk Items that are at risk for L3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return more nuanced errors

10 participants