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

Provide a mechanism to indicate backend registration success or failure #2067

Closed
Firehed opened this issue May 4, 2024 · 8 comments
Closed

Comments

@Firehed
Copy link

Firehed commented May 4, 2024

Description

When registering a new credential (in particular using passkeys), there exists a weird edge case where the browser APIs succeed but the backend processing fails or hangs. This can result in a confusing experience for users later on when authenticating - the browser can now suggest and even autofill a passkey that's completely unusable by the relying party.

I think it would be possible to alleviate this problem by expanding the navigator.credentials.create() API. In terms of ergonomics I'm not sure what would be best, but the gist of it would be adding something like discardIfNotConfirmedAfter: duration as a new optional parameter and, if provided, use it to hint that the credential isn't (yet) usable and should be removed from storage if a follow-up API isn't called. I could see something like navigator.credentials.confirm(credential.rawId) or an equivalent method added to the attestation that .create() returns)

Loosely:

const attestation = await navigator.credentials.create({
  publicKey: {
    discardIfNotConfirmedAfter: 30000, // milliseconds
    challenge,
    // ...
  }
})
const serverResponse = await storeCredentialWithRelyingParty(attestation) // Some site-specific procedure
// If this is not called with 30s of the .create() promise resolving AND the
// created credential is discoverable, then it could be discarded from storage
attestation.confirm() 

If the value is not provided, then the current behavior (of immediate storage) should be retained.

I've seen a couple discussions around adding APIs to edit existing credentials (to e.g. allow an RP to indicate to the browser that a previously-registered credential is no longer usable or should be renamed) that are generally in this same space.

@timcappalli
Copy link
Member

This can result in a confusing experience for users later on when authenticating - the browser can now suggest and even autofill a passkey that's completely unusable by the relying party.

@Firehed this scenario would be covered with a planned feature called the Report API.

(there is a superset issue tracking all these use cases: #1967)

@Firehed
Copy link
Author

Firehed commented May 4, 2024

Thanks for sharing! I'd done a pretty deep search on open issues but didn't think to check wiki pages. I'll be sure to look there as well in the future.

If I'm understanding the contents correctly, I don't see this exact case covered in here (I ran into it due to what seems like a recent Safari regression where the .create() promise sometimes doesn't resolve) but it does look to cover the same general area. Specifically, the decommissioning may be ineffective (or even impossible) since the RP wouldn't even know about the unregistered user and this could never relay the necessary information back to the browser.

Not sure what the best way to reach the authors to add this scenario into the discussion, but if you can help direct me I'd be more than happy to do so :)

@sbweeden
Copy link
Contributor

sbweeden commented May 4, 2024

The currentCredentials report type should cover your scenario it’s just a matter of when it is sent from the RP.

@arianvp
Copy link

arianvp commented May 4, 2024

I think this is the same issue as #2038

@timcappalli
Copy link
Member

@Firehed one of the challenges with anything in this space, including the Report API (which is why it will be opportunistic), is there can't be any expectation that the authenticator is still available after the ceremony completes. For example, the user may disconnect their security key, or they may have been using their phone which is no longer in range, which would mean the client would have to do some guessing on behalf of the authenticator, which is also not ideal.

In general, I like what you proposed, but unfortunately I think it would still be stuck in the opportunistic bucket, which means adding it would introduce additional complexity. The goal with the Report API is to tuck as many housekeeping capabilities as possible into this opportunistic call made by the RP.

@Firehed
Copy link
Author

Firehed commented May 4, 2024

@arianvp Yes, I think we're reporting effectively the same thing.

It was a browser bug rather than adverse network conditions that prompted me to suggest this, but the end result is about the same. There's also the (less important compared to end-user experience) matter of developers being stuck with dozens of passkeys when trying to initially build out an integration.

@timcappalli That's great additional context - and I fully agree that anything here is necessarily opportunistic (if only for BC reasons). I tried to touch on that with the "AND the created credential is discoverable" bit, but there's room for improvement in specifics. My thinking is probably best summarized as "opportunistic two-phase commit" though that's still somewhat of a misnomer.

After sleeping on the concept, I think the report API is (assuming widespread adoption and certain implementation details) enough to eventually resolve the issue, but I'd like to consider an additional real-world scenario before calling it sufficient:

  1. A user tries to register for a service using a passkey. In the event that a credential doesn't get registered with the RP (due to an implementation bug, bad network, etc), the user is almost certainly already having a bad and confusing experience. My real-world experience is getting stuck indefinitely on a loading spinner or equivalent, or being provided zero feedback whatsoever.
  2. After this happens, let's assume the user refreshes the page. The page's UI might be structured where there's a dual-purpose form, such as a register flow that also adds autocomplete="username webauthn" and runs the conditional flow to sign in. The user focuses the field and is prompted by the browser to use their (useless) credential
  3. That auth attempt of course fails, the user is perhaps shown an error message. They're now incredibly confused and frustrated. Perhaps they write to support (who's also confused, as they don't see an account for that user), or give up outright. They could be so annoyed that they swear off using passkeys on any other site.

Even if during the third step the RP uses the Report API1 in response to the failed auth attempt to invalidate that credential (which could result in a less-confusing second auth attempt and hinting the user towards trying to register again), their frustration and confusion levels are probably through the roof - likely to the point of giving up.

My thinking is that if there's an additional API to support this during registration, RPs can significantly reduce user confusion and frustration by heading off the problem after step 1 rather than after step 3.

This behavior is also more or less how password managers work today - they tend to not immediately save the password on form submit, but rather once the next network request comes back OK. What we have today for webauthn and specifically passkeys is in practice a step backwards on that narrow aspect, so I'm hoping we can close the gap in the next version of the spec!

Footnotes

  1. I'm also assuming that the API would always result in the credential being removed when called. Unless the spec were to indicate otherwise, I suspect browsers or credential managers might refuse to remove the last credential for an RP in an effort to prevent account lock-out. If they were to do that, the user is still left in the lurch.

@arianvp
Copy link

arianvp commented May 5, 2024

After this happens, let's assume the user refreshes the page. The page's UI might be structured where there's a dual-purpose form, such as a register flow that also adds autocomplete="username webauthn" and runs the conditional flow to sign in. The user focuses the field and is prompted by the browser to use their (useless) credential

A dual-purpose registration / signin form is the recommended UX by FIDO Alliance. https://fidoalliance.org/ux-guidelines/

Effectively trapping people in a broken authentication loop.

I agree that discoverable credentials should have a two-phase commit on registration. Anything else will lead to users getting extremely confused and locked out of registration and authentication flows

@emlun
Copy link
Member

emlun commented May 15, 2024

2024-05-15 WG call: closing as duplicate of #1967 as mentioned above. This will be addressed by the WebAuthn Reporting API.

@emlun emlun closed this as completed May 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants