Migrating passkeys to a new domain
Passkeys are resistant to phishing attacks because they’re bound to a specific domain (RP ID). This does, however, raise some serious issues:
-
Domain migration - at some point you may need to change your domain. If your passkeys are registered to
oldsite.com, how will your users sign in onnewsite.com? -
Related domains - users may legitimately want to present a passkey from a different domain e.g. presenting an
example.compasskey toexample.co.uk.
Fortunately the passkey specs now support Related Origin Requests, allowing you to accept passkeys bound to other domains.
Related Origin Requests
Section titled “Related Origin Requests”For the examples we’ll use two domains: oldsite.com and newsite.com.
Assume existing passkeys are registered to oldsite.com, and you want to migrate your domain to newsite.com. You have a couple of options:
Continue using the existing Relying Party ID on the new domain
Section titled “Continue using the existing Relying Party ID on the new domain”The simplest option is to continue using an RP ID of oldsite.com on newsite.com.
1. Authorize passkey ceremonies with the old RP ID
Section titled “1. Authorize passkey ceremonies with the old RP ID”When your backend authorizes registration or authentication, keep passing oldsite.com as the RP ID:
const result = await passlock.authorizePasskeyRegistration({ rpId: "oldsite.com",});const result = await passlock.authorizePasskeyAuthentication({ rpId: "oldsite.com",});2. Whitelist the new domain
Section titled “2. Whitelist the new domain”Host a /.well-known/webauthn file at the root of oldsite.com (the RP ID domain), whitelisting newsite.com:
{ "origins": [ "https://oldsite.com", "https://newsite.com" ]}The trade-offs
Section titled “The trade-offs”This is the simplest approach, for users and developers. However, there are some drawbacks:
-
You need to host
https://oldsite.com/.well-known/webauthnindefinitely. -
Some browsers/devices ignore the Relying Party Name and display the RP ID to users, so they will see the message “do you want to sign in with your oldsite.com passkey?” or similar.
Migrate to a new RP ID
Section titled “Migrate to a new RP ID”Alternatively, you can use newsite.com for new passkeys, whilst continuing to accept passkeys registered to oldsite.com:
1. Register new passkeys with the new RP ID
Section titled “1. Register new passkeys with the new RP ID”For new or replacement passkeys, register them with newsite.com instead of oldsite.com.
const result = await passlock.authorizePasskeyRegistration({ rpId: "newsite.com",});2. Whitelist the new domain
Section titled “2. Whitelist the new domain”Host a /.well-known/webauthn file at the root of oldsite.com, whitelisting newsite.com:
{ "origins": [ "https://oldsite.com", "https://newsite.com" ]}3. Accept legacy passkeys
Section titled “3. Accept legacy passkeys”By default, you should authenticate passkeys with the new newsite.com RP ID. Newly created passkeys will work fine.
const result = await passlock.authorizePasskeyAuthentication({ rpId: "newsite.com",})To authenticate legacy oldsite.com passkeys you will need to pass oldsite.com as the RP ID:
const result = await passlock.authorizePasskeyAuthentication({ rpId: "oldsite.com",})This tells Passlock to fetch authentication options for oldsite.com, allowing the browser to present passkeys that were originally created for that RP ID.
4. Migrate legacy passkeys
Section titled “4. Migrate legacy passkeys”It’s a good idea to prompt legacy users to create a new passkey, which will be registered against the new RP ID. The best time to do this is usually following a successful login with a legacy passkey:
const result = await passlock.authorizePasskeyRegistration({ rpId: "newsite.com",})The trade-offs
Section titled “The trade-offs”This approach allows you to support passkeys registered to an old domain, while also registering passkeys against the new domain.
After a user has authenticated with a “legacy” passkey, you can prompt them to register a new one, using the current domain/RP ID. You can even delete the old passkey for them. The downsides are:
-
You need to host
https://oldsite.com/.well-known/webauthnuntil all users have been migrated. -
You still need to decide whether to request a passkey for the old or new RP ID. There is currently no single request that asks the browser to present passkeys from both domains.