Skip to content

Relying Party ID (rpId)

In the WebAuthn specification, your application is called a Relying Party — you are relying on the passkey to verify the user’s identity. The Relying Party ID (RP ID) is the domain your passkeys are cryptographically bound to.

Because passkeys are bound to a specific RP ID, a passkey registered on legit.com cannot be used to authenticate on evil.com. This is key to passkey phishing resistance.

The browser origin combines the scheme, hostname, and port e.g. https://app.example.com:443. The RP ID is just the effective domain component, with no scheme or port e.g. app.example.com.

The WebAuthn specification requires the RP ID to be either the page’s exact hostname, or a registrable domain suffix of it. This means:

Browser originValid RP ID values
https://app.example.com app.example.com, example.com
https://example.comexample.com

Your backend supplies the rpId when it calls authorizePasskeyRegistration or authorizePasskeyAuthentication. Passlock includes that value in the WebAuthn options sent to the browser and stores it with the resulting credential metadata.

backend/passkeys.ts
await passlock.authorizePasskeyRegistration({
rpId: "example.com",
userId: user.id,
username: user.email,
});
await passlock.authorizePasskeyAuthentication({
rpId: "example.com",
discoverable: true,
});

Passlock validates that the supplied rpId is syntactically valid, but the browser enforces whether the current origin is allowed to use it. This means you should choose the RP ID deliberately for each authorize* call:

  1. Use your app hostname for most production and staging flows.
  2. Use localhost for local development.
  3. Use the RP ID that the existing passkey was registered against when supporting domain migrations.

Existing passkeys remain bound to the RP ID used during registration. If your domain changes, a single tenancy can temporarily contain passkeys registered against both old and new RP IDs.

See domain migration for the migration patterns.

You can register and authenticate passkeys on http://localhost or https://localhost by passing rpId: "localhost" from your backend. No extra Passlock configuration is needed.