Best Practices for Passkeys
Here are our official recommendations when adding passkey support to your application.
Every application is unique, so not everything here will necessarily apply to you. If you have a reason to do a different thing, that's fine - but be informed!
Let users create multiple passkeys
Some of your users are probably in multiple passkey "ecosystems" (e.g. a Windows laptop and an iPhone). At present, there are limited options to sync passkeys across ecosystems, similar to the experience of using a system password manager. While cross-device authentication works well, it's somewhat tedious to do daily.
If you allow users to create multiple passkey, users in this situation will have a much better sign-in experience.
Additionally, for users using non-passkey WebAuthn (like a USB or NFC hardware key), it's critical to have a backup device in case the primary is lost or damaged. Don't assume that all WebAuthn usage is passkeys! By default, SnapAuth allows users to register any kind of authenticator.
Let users (re)name their passkeys
For users that have registered multiple passkeys, it's very important to be able to tell them apart!
The AAGUID
of the credential provides a strong hint for a default value, but we recommend letting users rename them as they see fit.
We have improved support for this on our roadmap, but if you're running your own WebAuthn services, you'll need to handle it yourself.
Use progressive enhancement
If you have an existing site and are starting to adopt passkey support, most of your users are probably still signing in with a password.
By adding autocomplete="username webauthn"
into your <input>
field for the username of your sign in page, you can leverage the autofill API.
This will prompt users that do have passkeys to use the one-click experience, and those without will see the same UI they're used to.
You will still want to have a way to explicitly use a passkey to sign in, especially since this UI is generally not available with removable keys. Consider how many of your users have passkeys configured when deciding how prominent to make this.
Reconsider your security model
Passkeys (and WebAuthn as a whole) are a huge step up from passwords in terms of overall security. It's important to be aware of how they differ from passwords from a security perspective:
- Passkeys are usually, but not always, protected by biometrics1. Sometimes they are protected by a PIN or password. When this is the case, they are two-factor auth2 in a single step.
- Passkeys are domain-specific. This effectively eliminates phishing. If you run your application on multiple domains, this can get tricky to navigate.
- Passkeys are a "possession" factor; passwords are a "knowledge" factor. This can have different legal implications.
- Passkeys can be shared, just like passwords.
If you have a password-first site, passkeys can be used purely as a second factor. They're an incredibly low-friction way to add MFA, without changing your initial sign-in experience at all.
If you want to go passwordless, passkeys should be at the top of your list for consideration.
At SnapAuth, we'll happily discuss your needs, even if that means NOT using our service. Get in touch if want a free, no-pressure consultation.
Handle errors
Many passkey demos and examples omit most or all error handling in order to keep code samples smaller and easier to follow. But WebAuthn APIs can fail in a large number of scenarios:
- The user hit cancel when prompted to create or use a passkey
- The user's hardware authenticator is damaged
- There was a network error
- The WebAuthn request timed out (this is not from a network error)
- The user is trying to sign in from another person's device, and doesn't have their own to use Cross-Device Authentication
- The user's device doesn't support WebAuthn at all (limited to fairly old devices)
SnapAuth makes this significantly easier, but don't completely skip it!
Don't abandon account recovery flows
While passkeys tend to be far more reliable than passwords when it comes to users not getting locked out of their own account, you still need to support your existing users. Plus, there are two WebAuthn-specific situations that are common enough at scale that need to be handled:
- A user has changed device ecosystems3
- A user registered a hardware key, and it's been lost or damaged
You probably already have this process in place, so there may be nothing to do here! However, you may want to adjust the final step to allow for adding or replacing a passkey, instead of only setting a new password.
Don't rely on or require attestation
For most applications (and nearly all public-facing ones), enforcing any sort of credential attestation requirements is going to cause problems.
Plus, as of May 2024, Apple devices and operating systems always ignores attestation requests and generates passkeys with the none
format.
This means that if you require attestation, you're effectively blocking all Apple users.
Exception:
For high-security internal applications, you may want to require attestation. This should only be done when you are issuing authenticators to users, and you're restricting usage to those issued authenticators.
As a general rule, if it's not something behind a corporate VPN, avoid attestation requirements.
Keep up to date
The browser APIs to support passkeys are on their third major revision, with many more changes planned or under discussion.
If you're directly implementing WebAuthn support, make sure to understand the difference between the various versions of the specs - you need to support all of them. There's subtle differences across browsers, and they occasionally ship regressions.
If you're using SnapAuth, keep our SDKs up to date. We're slightly obsessed with semantic versioning, so you can upgrade with confidence.
Help credential managers
When creating a new credential, use the user's sign-in handle in the registration API call:
const registration = await snapAuth.startRegister({
username: '[email protected]',
})
This helps ensure that there's a single entry in credential managers, especially if the user already has a password-based account.
There's an optional displayName
field you can also provide to show other values, such as a given name - but not all platforms use it.
Adopting the Open Graph site name tag can enhance the appearance of your site or app in credential managers, by replacing the domain with a more human-friendly version:
<meta property="og:site_name" content="Your Site Name" />
Credential managers are rather vague about the source of displayed site icons/imagery.
In our testing, having a favicon (preferably SVG) and apple-touch-icon
seems generally sufficient and reliable.
<link rel="icon" href="/favicon.ico" sizes="32x32" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
SnapAuth makes passkeys easy
If you want to run your own WebAuthn infrastructure, awesome! You're still making the internet a more secure place.
If you'd rather let us handle the ever-changing landscape for you, we're here for you. This is all we do and it's why we exist.
Biometric authentication is done on-device. Your application will NEVER see biometric data (nor will SnapAuth). ↩
You'll know that it's two-factor, but there's no current way to know which other factor (knowledge or inherence) was used. W3C is exploring an addition to the WebAuthn APIs to clarify this. ↩
iPhone to Android, etc. Support for cross-ecosystem exporting and syncing is being developed. ↩