Using passkeys as a primary login mechanism
Tags: webdev, passkeys
Published: 2024-03-19 16:48:30

In my [previous post][passkeys] I explained how to use passkeys for secondary authentication (MFA). In this post I'll show how we can replace passwords completely, and use passkeys for primary and secondary authentication.

## Why write this post?

If you've read my previous post you might think it's easy to use passkeys for primary authentication. Just register a passkey during account creation and you're good to go. Not so fast! There are a few things you need to think about first.

**What if the user disables Javascript?**

Passkeys rely on a browser API. There is currently no way to register or authenticate with a passkey if the user disables Javascript. 

**What if a user deletes their passkey?**

Unlike a password, a user can't forget a passkey but they can delete it. Admittedly it's not something they're likely to do by accident but it could happen.

**What if the user moves platforms?**

This is a more likely scenario - a user decides they've had enough of Android and they're moving to Apple land (or vica versa). To the best of my knowledge there's no way for a user to migrate the private keys associated with a passkey to a new platform.

**What if you change your hostname?**

Passkeys are resisitant to phishing attacks because the device enforces a same site policy. A passkey created for example.com can't be used on exxample.com, even if the user wants to. Unfortunately this also means a passkey created for old.com can't be used on new.com, even if you control both domains.

There are proposals to deal with hostname/domain migration and they are gaining support. The most likely solution appears to be the trusted hosts concept, by which a website can specify other hosts on which the passkeys can be used. E.g. a site would create a document at `/.well-known/trusted-sites.txt` listing the other sites. 

## You need a fallback

It's clear that by relying on passkeys alone you're taking quite a risk, however that doesn't mean you need to continue using passwords. You just need a fallback option. Suitable fallbacks include:

**Reset passkey emails**

Essentially the same as the reset password flow. You allow the user to request a one time code or secure link. They can then use this to login and register a new passkey. This does of course assume you trust their mailbox security and it's unlikely to be suitable in a regulated environment.

**Social login**

Encourage the user to link a social account e.g. Google, GitHub or Twitter (sorry X!).

**Administrator reset**

If you're working in an intranet or extranet environment, you could allow adminstrators to "reset" the passkey. The admin won't be able to actually register the passkey on the device but they can generate and share a secure link to do so.

**TOTP authenticator**

Another option is to require users to register a TOTP based authenticator (e.g. Google Authenticator) during (or after) account creation. I'm not a fan of TOTP authenticators because copying and pasting one time codes is a pain. However users would use passkeys as their primary means of authentication, resorting to Google authenticator only if and when they need to register a new passkey.

**Mix and match**

The best approach is likely to be a combination of two fallback mechanisms, and my recommendation would be a reset passkey email + TOTP one time code. In fact I'm adding this capability to the Passlock library. Usability is not really an issue because 99% of the time users will just log in with their passkeys. 

## Conclusion

Passkeys offer strong, frictionless authentication and are a definite improvement over passwords. However developers should think twice before relying on passkeys alone. A fallback mechanism should be put in place to allow for account recovery in case a passkey is deleted, or the website hostname changes.

Account recovery is an exceptional event so it should trigger further notifications and alerting. At the very least an email should be sent to the account holder informing them that a new passkey was created.

[passkeys]: https://dev.to/toby_hobson/adding-passkey-authentication-to-web-apps-ai1