![Maud Nalpas](https://web.dev/images/authors/maudnalpas.jpg) Maud Nalpas [GitHub](https://github.com/maudnals) [LinkedIn](https://www.linkedin.com/in/maudnalpas) [Bluesky](https://bsky.app/profile/maudnals.bsky.social)

<br />

> [!CAUTION]
> **Caution:** One or more demos on this page no longer work, as [Glitch has ended hosting](https://blog.glitch.com/post/changes-are-coming-to-glitch).

[Passkeys](https://developer.chrome.com/docs/identity/passkeys) are tied to a
specific website, and they can only be used to sign in on the website they were
created for.

This is specified in the [relying party
ID](https://www.w3.org/TR/webauthn-2/#rp-id) (RP ID), which for passkeys created
for the `example.com` domain could be `www.example.com` or `example.com`.

While RP IDs prevent passkeys from being used as a single credential for
authenticating everywhere, they create issues for:

- **Sites with multiple domains** : Users can't use the same passkey to sign in across different country-specific domains (for example `example.com`, and `example.co.uk`) managed by the same company.
- **Branded domains** : Users can't use the same credential across different domains used by a single brand (for example `acme.com` and `acmerewards.com`).
- **Mobile apps**: Mobile apps often don't have their own domain, making credential management challenging.

There are workarounds based on identity federation, and others based on
iframes, but they are inconvenient in some cases. Related Origin Requests offer
a solution.

> [!NOTE]
> **Note:** RP IDs do separate credentials between different websites, but phishing protection actually comes from verifying the website's origin in the [`CollectedClientData`](https://www.w3.org/TR/webauthn-3/#dictionary-client-data) field.

### Solution

With
[Related Origin Requests](https://w3c.github.io/webauthn/#sctn-related-origins),
a website can specify origins allowed to use its RP ID.

This allows users to reuse the same passkey across multiple sites you operate.

To use Related Origin Requests, you need to serve a special JSON file at a
specific URL `https://{RP ID}/.well-known/webauthn`. If `example.com` wants to
allow the additional origins to use it as an RP ID, it should serve the following
file at `https://example.com/.well-known/webauthn:`

```json
{
  "origins": [
    "https://example.co.uk",
    "https://example.de",
    "https://example-rewards.com"
  ]
}
```

Next time any of these sites makes a call for passkey creation
(`navigator.credentials.create`) or authentication (`navigator.credentials.get`)
that uses `example.com` as an RP ID, the browser will notice an RP ID that
mismatches the requesting origin. If the browser supports Related Origin
Requests, it first looks for a
`webauthn` file at `https://{RP ID}/.well-known/webauthn`. If the file exists,
the browser checks whether the origin making the request is on the `allowlist`.
If yes, it proceeds to passkey creation or authentication steps.

If the browser doesn't support Related Origin Requests, it throws a `SecurityError`.

## Browser support

Related Origin Requests are supported on Chrome and Safari. As of January 2026, Firefox is still considering the feature. You can track its status in the [Mozilla standards position issue](https://github.com/mozilla/standards-positions/issues/1052).

## Set up Related Origin Requests

The following demo uses two sites, `https://ror-1.glitch.me` and `https://ror-2.glitch.me`.
To enable users to sign in with the same passkey across both of those sites, it uses Related Origin Requests to allow `ror-2.glitch.me` to use `ror-1.glitch.me` as its RP ID.

### Demo

> [!NOTE]
> **Note:** Open the demo in one of the [supported browsers](https://web.dev/articles/passkey-related-origin-requests#browser_support).

[https://ror-2.glitch.me](https://ror-2.glitch.me/) implements Related Origin Requests to use ror-1.glitch.me as an RP ID, so both `ror-1` and `ror-2` use `ror-1.glitch.me` as an RP ID upon creating a passkey or authenticating with it.
We've also implemented a shared passkey database across these sites.

Observe the following user experience:

- You can successfully create a passkey, and authenticate with it, on `ror-2`---even though its RP ID is `ror-1` (and not `ror-2`).
- Once you create a passkey on either `ror-1` or `ror-2`, you can authenticate with it on both `ror-1` and `ror-2`. Because `ror-2` specifies `ror-1` as an RP ID, making a passkey creation or authentication request from any of these sites is the same as making the request on ror-1. The RP ID is the only thing that ties a request to an origin.
- Once you create a passkey on either `ror-1` or `ror-2`, it can be auto-filled by Chrome on both `ror-1` and `ror-2`.
- A credential created on any of these sites will have an RP ID of `ror-1`.

![Chrome autofills on both sites.](https://web.dev/static/articles/webauthn-related-origin-requests/image/chrome-autofill.jpg) Thanks to Related Origin Requests, users can use the same passkey credential across ror-1 and ror-2. Chrome will also autofill the credentials.

See code:

- See the `/.well-known/webauthn` file set up in the [ror-1 codebase](https://glitch.com/edit/#!/ror-1?path=server.mjs%3A142%3A11).
- See `RP_ID_ROR` occurrences in the [ror-2 codebase](https://glitch.com/edit/#!/ror-2?path=libs%2Fauth.mjs%3A216%3A35).

> [!NOTE]
> **Note:** In this demo, for simplicity, no password check actually happens under the hood. The demo focuses on passkeys.

### Step 1: Implement a shared account database

If you want your users to be able to sign in with the same passkey across
`site-1` and `site-2`, implement an account database that is shared across these
two sites.

### Step 2: Set up your .well-known/webauthn JSON file in site-1

First, configure `site-1.com` such that it allows `site-2.com` to use it as an
RP ID. To do so, create your webauthn JSON file:

```json
{
    "origins": [
        "https://site-2.com"
    ]
}
```

The JSON object must contain key named origins whose value is an array of one
or more strings containing web origins.

#### Important limitation: Maximum 5 labels

Each element of this list will be processed to extract the eTLD + 1 *label* .
For example, the eTLD + 1 labels of `example.co.uk` and `example.de` are both
`example`. But the eTLD + 1 label of `example-rewards.com` is `example-rewards`.
In Chrome, the maximum number of labels is 5.

> [!WARNING]
> **Warning:** If your list includes too many labels (more than 5), the excessive element(s) will be ignored.

### Step 3: Serve your .well-known/webauthn JSON in site-1

Then, serve your JSON file under `site-1.com/.well-known/webauthn`.

> [!CAUTION]
> **Caution:** The file must be served with a content-type of `application/json` and contain a single [JSON](https://datatracker.ietf.org/doc/html/rfc8259) object listing allowed origins. The correct format for the file is: `/.well-known/webauthn` (do not include the `.json` extension).

For example, in express:

```javascript
app.get("/.well-known/webauthn", (req, res) => {
  const origins = {
    origins: ["https://site-2.com"],
  };
  return res.json(origins);
});
```

Here, we're using express [`res.json`](https://expressjs.com/en/api.html#res.json), which already sets the
correct `content-type` (`'application/json'`);

### Step 4: Specify the RP ID in site-2

In your `site-2` codebase, set `site-1.com`as the RP ID everywhere needed:

- Upon credential creation:
  - Set `site-1.com` as the RP ID in the credential creation `options` that are passed to the `navigator.credentials.create` frontend call, and typically generated server-side.
  - Set `site-1.com`as the expected RP ID, as you run credential verifications before saving it to your database.
- Upon authentication:
  - Set `site-1.com` as the RP ID in the authentication `options` that are passed to the `navigator.credentials.get` frontend call, and typically generated server-side.
  - Set `site-1.com`as the expected RP ID to be verified on the server, as you run credential verifications before authenticating the user.

> [!NOTE]
> **Note:** What about the `origin`? And the RP `name`? In your site-2 codebase, the expected `origin` should remain unchanged (`https://site-2.com`), because it designates where the request is expected to come from. As per the RP name (`rp.name`), it's unused in practice. You can leave it empty.

### Troubleshooting

![Error message popup in Chrome.](https://web.dev/static/articles/webauthn-related-origin-requests/image/error-file-not-found.jpg) Error message in Chrome upon credential creation. This error is thrown if your \`.well-known/webauthn\` file can't be found at \`https://{RP ID}/.well-known/webauthn\`. ![Error message popup in Chrome.](https://web.dev/static/articles/webauthn-related-origin-requests/image/error-file-incorrect.jpg) Error message in Chrome upon credential creation. This error is thrown if your \`.well-known/webauthn\` file can be found, but doesn't list the origin you're trying to create a credential from.

## Other considerations

### Share passkeys across sites and mobile apps

Related Origin Requests allow your users to reuse a passkey across *multiple
sites* .
To allow your users to reuse a passkey across a *website* and a *mobile app*,
use the following techniques:

- In Chrome: [Digital Asset
  Links](https://developers.google.com/digital-asset-links). Learn more at [Add support for Digital Asset Links](https://developer.android.com/identity/sign-in/credential-manager#add-support-dal).
- In Safari: [Associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains).

### Share passwords across sites

Related Origin Requests allow your users to reuse a *passkey* across sites.
Solutions for sharing *passwords* across sites vary between password managers.
For Google Password Manager, use [Digital Asset Links](https://developer.chrome.com/docs/identity/site-affiliation).
Safari has [a different system](https://github.com/apple/password-manager-resources#shared-credentials).

### Role of credential managers and user agents

This goes beyond your scope as a site developer, but note that in the longer
term, the RP ID shouldn't be a user-visible concept in the user agent or the
credential manager your users are using. Instead, user agents and credential
managers should show users *where* their credentials have been used. This change
will take time to implement. A temporary solution would be to display both the
current website and the original registration site.