---
url: 'https://www.corbado.com/zh/blog/webauthn-relying-party-id-rpid-passkeys'
title: 'WebAuthn 依赖方 ID (rpID) 与通行密钥：域名与原生应用'
description: '本文将介绍用于通行密钥身份验证的 WebAuthn 依赖方 ID。文章概述了正确的配置、域名匹配以及原生应用的配置方法。'
lang: 'zh'
author: 'Vincent Delitz'
date: '2026-06-15T13:54:56.152Z'
lastModified: '2026-06-16T06:04:32.556Z'
keywords: '依赖方 ID, 通行密钥, WebAuthn, 域名匹配, 原生应用'
category: 'WebAuthn Know-How'
---

# WebAuthn 依赖方 ID (rpID) 与通行密钥：域名与原生应用

## Key Facts

- \*\*依赖方 ID（Relying Party
  ID）\*\*是嵌入在每个通行密钥（passkey）中的域名。如果浏览器源（origin）不匹配，身份验证就会失败，这使得通行密钥具有极强的抗网络钓鱼能力。
- **RP ID** 绝不能位于\*\*公共后缀列表（Public Suffix
  List）\*\*中，而且更改它会使所有现有的通行密钥失效。默认情况下，请使用根域名来适应未来的发展需求。
- iOS 应用要求在 RP ID 下的 `/.well-known/` 目录中放置一个 **apple-app-site-association**
  文件。Android 则要求在同一路径下放置
  **assetlinks.json**。新文件最多可能需要 24 小时才能被缓存。
- 多个根域名需要通过 `.well-known/webauthn` 配置\*\*相关源请求（Related Origin
  Requests）\*\*才能共享通行密钥。为所有应用（无论是 Web 应用还是原生应用）使用单一的 WebAuthn 服务器和一个相同的 RP
  ID。

## 1. 简介

通行密钥身份验证正迅速成为常态，[TikTok](https://www.corbado.com/blog/tiktok-passkeys)、GitHub、[WhatsApp](https://www.corbado.com/blog/whatsapp-passkeys)、X（[Twitter](https://www.corbado.com/blog/x-twitter-passkeys)）、[LinkedIn](https://www.corbado.com/blog/linkedin-passkeys)
和亚马逊等科技巨头都在纷纷推出或已经实施了该功能。显然，科技界正在认识到简单而安全的身份验证的重要性。

除了在使用面容 ID（[Face ID](https://www.corbado.com/faq/is-face-id-passkey)）、触控 ID（Touch ID）或
[Windows Hello](https://www.corbado.com/glossary/windows-hello)
身份验证时带来的无缝用户体验之外，与密码等传统身份验证方法相比，通行密钥提供了无与伦比的安全性。其最突出的特性之一就是强大的抗网络钓鱼能力。

网络钓鱼攻击是指诱骗受害者将凭证提供给伪装成原始网站的假冒网站的攻击。例如，想象一下，您收到一封看似来自银行的电子邮件，要求您登录。您点击了链接，网站看起来很合法，于是您输入了凭证，随后攻击者就可以利用这些凭证登录原始网站。在数字时代，这正成为一个日益严重的问题，甚至连
[Okta](https://www.darkreading.com/application-security/okta-flaw-involved-mgm-resorts-breach-attackers-claim)（一家身份验证提供商！）或
[Retool](https://retool.com/blog/mfa-isnt-mfa/)
这样的大公司也成为了鱼叉式网络钓鱼攻击（一种特殊形式的网络钓鱼，针对单一受害者使用极其个性化的钓鱼手段）的受害者，这凸显了采取强大安全措施的必要性。

相反，如果您使用通行密钥，当网站是假冒的，您的身份验证就会失败。这是因为通行密钥通过\*\*依赖方 ID（[Relying Party](https://www.corbado.com/glossary/relying-party)
ID）\*\*与为其创建的域名绑定在一起。

> 您无法在任何其他网站上使用某个通行密钥进行登录，这使得通行密钥具有极强的抗网络钓鱼能力（尽管没有任何系统能够绝对免疫所有的攻击媒介）。

这种机制被融入到了 WebAuthn 中，WebAuthn 是通行密钥底层用于无密码身份验证的 Web 标准。WebAuthn 建立在两个核心仪式（ceremony）之上：注册和身份验证仪式。

在注册（注册新账号）仪式中，系统会通过 Web 应用或原生应用为在线服务（即依赖方）创建一个新的通行密钥。在这个过程中，为该通行密钥创建所在的域名（依赖方 ID）会被存储在通行密钥中。

这就允许身份验证（登录）仪式去检查 Web 或原生应用所属的在线服务（依赖方）是否与存储在通行密钥中的依赖方 ID 相匹配。

在接下来的内容中，我们将详细了解这种[域名匹配](#what-relying-party-id)过程是如何运作的，特别是原生应用是如何获得安全保障的。

## 2. 什么是依赖方 ID？

[**依赖方 ID**](https://www.w3.org/TR/webauthn-2/#relying-party-identifier)本质上是一个存储在通行密钥中的域名，它能确保通行密钥仅在当前浏览器 URL（即用户的源，会自动在每次请求中发送）与其匹配时才起作用（请参考[下面](#integration-options-native-apps)的原生应用方法）。它是 WebAuthn 规范的一个关键组成部分，您可以[在此处](https://www.w3.org/TR/webauthn-2/)深入了解。依赖方 ID 可以是根域名（例如
`corbado.com`）或子域名（`auth.corbado.com`）。如果根域名在公共后缀列表中，您就无法将其存储为依赖方 ID（[在此处](https://publicsuffix.org/learn/)可以找到该列表以及用于检测公共后缀的库）。更改某项在线服务的依赖方 ID 会导致现有的通行密钥失效（例外情况：新的依赖方 ID 是旧依赖方 ID 的子域名，或者通过
`.well-known/webauthn` 配置了相关源请求以允许在相关域名间重复使用通行密钥）。

在身份验证过程中，系统会将依赖方 ID 与浏览器 URL（用户的源）进行检查以确保它们匹配。在这个意义上，匹配意味着以下其中一种情况：

- 浏览器 URL（用户的源）与依赖方 ID 精确匹配，或者
- 浏览器 URL（用户的源）是与依赖方 ID 匹配的子域名，并且父域名是可注册的（例如，`com`
  或公共后缀列表中的任何域名都不行）

下面是详细的概述，说明了哪些 _originalHost_（第二列）被允许访问其父域名：

![依赖方 ID 表格](https://www.corbado.com/website-assets/relying_party_id_table_4c98cc04f4.jpg)

在下面，您可以看到解析后的
[**PublicKeyCredentialCreationOptions**](https://www.w3.org/TR/webauthn-2/#iface-pkcredential)：

```json
{
    "publicKey": {
        "attestation": "direct",
        "authenticatorSelection": {
            "authenticatorAttachment": "platform",
            "requireResidentKey": false,
            "userVerification": "preferred"
        },
        "challenge": "qNqrdXUrk5S7dCM1MAYH3qSVDXznb-6prQoGqiACR10=",
        "excludeCredentials": [],
        "pubKeyCredParams": [
            {
                "alg": -7,
                "type": "public-key"
            }
        ],
        "rp": {
            "id": "corbado.com",
            "name": "Corbado"
        },
        "timeout": 30000,
        "user": {
            "displayName": "Corbado user",
            "id": "bz9ZDfHzOBLycqISTAdWwWIZt8VO-6mT3hBNXS5jwmY="
        }
    }
}
```

`rp` 代表依赖方（[Relying Party](https://www.corbado.com/glossary/relying-party)）。

依赖方 ID 的主要优点之一是可以防止网络钓鱼攻击。想象一下以下场景：

1. 攻击者开发了一个 [PayPal](https://www.corbado.com/blog/paypal-passkeys)
   的克隆网站，这是一个假冒网站，试图窃取您的 [PayPal](https://www.corbado.com/blog/paypal-passkeys)
   凭证，以您的名义登录并将钱转入攻击者的账户。这个假冒的 [PayPal](https://www.corbado.com/blog/paypal-passkeys)
   网站托管在域名 paybal.com 上（因此乍一看通常无法发现它是不同的域名）。
2. 您过去已经为合法的 PayPal 网站创建过通行密钥。这个通行密钥存储的依赖方 ID 是 paypal.com。
3. 您访问了 paybal.com 上的假冒 PayPal 网站（当您访问这个网站时，您请求的源是 paybal.com\*），该网站向您的设备（身份验证器）发送一个针对依赖方 ID
   `paypal.com` 的质询（challenge）（在这里它试图欺骗您），并要求您用针对依赖方 ID
   paypal.com 的通行密钥对其进行签名。
4. 您的设备（身份验证器）会检查请求的源（`paypal.com`）与存储在通行密钥中的依赖方 ID（`paypal.com`）是否匹配。
5. 由于它们显然不匹配，身份验证将失败，从而保护您避免将 PayPal 账户的访问权限提供给某个攻击者。

下图说明了这种防止网络钓鱼的机制。

_如果是原生应用，源的处理方式因平台而异：在 Android 上，源的格式为
`android:apk-key-hash:<SHA256_fingerprint>`。在 iOS 上，WebAuthn 的源是 RP 的 web 源（例如
`https://paypal.com`）。在这两种情况下，您的原生应用和服务器之间的信任都是通过相应的关联文件来验证的（参见[下面](#configuring-relying-party-native-apps)的内容）。有关原生应用的源验证如何运作的详细信息，请参阅我们针对原生应用 WebAuthn 源验证的专门指南。_

## 3. 针对原生应用的两种不同的集成选项

要在原生应用中实施通行密钥，开发人员可以选择通过 [WebView](https://www.corbado.com/blog/native-app-passkeys)
或原生方式添加。下面我们来看看这两种方法的优缺点。

### 3.1 通过 WebView 集成通行密钥

![WebView 通行密钥集成（GitHub）](https://www.corbado.com/website-assets/650c601b1eb462e25702a870_android_webview_passkey_github_8bc81d9852.jpg)

使用
**WebView**\* 来集成通行密钥意味着在原生应用中嵌入一个网页浏览器以处理身份验证过程。这种方法本质上是在应用内部显示一个网页，从而更容易重用基于 Web 的身份验证流程，而无需为原生平台重写它们。然而，这种做法也存在一些缺点。[WebView](https://www.corbado.com/blog/native-app-passkeys)
可能不支持所有的通行密钥功能，并且如果没有安全地实施，就存在“中间人”攻击的潜在风险。此外，用户体验可能不如原生集成那么流畅，而且在维护跨不同设备和操作系统版本的一致行为方面可能会面临挑战。

_\*有多种类型的 WebView：在 iOS 上有 WKWebView、SFSafariViewController 或 SFAuthenticationSession
/ ASWebAuthenticationSession（用于基于 OAuth/OpenID
Connect 的身份验证流程），在 Android 上有 WebView、CCT-Chrome 自定义选项卡（Chrome Custom
Tabs）。有关详情，请参阅这篇博文。如果您不想使用原生集成，我们建议在通行密钥上下文中结合使用 SFSafariViewController/
ASWebAuthenticationSession 和 Chrome 自定义选项卡。_

### 3.2 原生通行密钥集成

![原生通行密钥集成（Kayak）](https://www.corbado.com/website-assets/650c625cac723f594137a029_android_native_passkey_kayak_0f689e761e.jpg)

**原生集成**涉及使用特定于平台的 API 和库将通行密钥功能直接构建到
[iOS](https://www.corbado.com/blog/how-to-enable-passkeys-ios) 或 [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android)
应用中。这种方法提供了更无缝的用户体验，因为无需在原生应用和
[WebView](https://www.corbado.com/blog/native-app-passkeys)
之间进行转换。它还实现了更好的性能和更一致的外观与感觉。从安全的角度来看，原生集成可以针对某些类型的攻击提供增强的保护，特别是在结合了特定平台的安全功能时。然而，实施工作量可能会更高，因为开发人员需要为每个平台（[Android](https://www.corbado.com/blog/how-to-enable-passkeys-android)
和
[iOS](https://www.corbado.com/blog/how-to-enable-passkeys-ios)）编写和维护独立的代码。此外，了解最新的通行密钥 /
WebAuthn 规范可能需要更频繁的应用更新。

接下来，我们将重点关注原生的通行密钥集成。

## 4. 为原生应用配置依赖方

与 Web 应用相比，原生应用（例如 iOS 或 [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android)
应用）存在一项挑战。与 Web 应用不同，原生应用没有可与依赖方 ID 进行匹配的浏览器 URL。尽管如此，为确保获得同等级别的安全性，域名需要通过\*\*关联文件（association
files）\*\*与原生应用连接，从而在域名和原生应用之间建立信任。

如果通行密钥是在 Web 应用上创建的并且应该在原生应用上用于相同的依赖方 ID（反之亦然），这一点尤其重要。

### 4.1 iOS 上的关联文件：apple-app-site-association

iOS 使用 apple-app-site-association 文件。该文件包含各种授权（entitlements），但对于 WebAuthn 和通行密钥而言，webcredentials 授权非常重要。

```json
{
    "webcredentials": {
        "apps": ["9RF9KY88B2.com.corbado.passkeys"]
    }
}
```

在 webcredentials.apps 中，您需要存储您的应用标识符前缀（Application Identifier
Prefix）（例如 `9RF9KY77B2`）和捆绑包标识符（Bundle Identifier）（例如
`com.corbado.passkeys`）。

要让 iOS 原生应用正常运行，apple-app-site-association 文件必须存储在依赖方 ID 的
`/.well-known` 目录下（`https://<依赖方 ID>/.well-known/apple-app-site-association`）。

[在此处](https://pro-6244024196016258271.frontendapi.cloud.corbado.io/.well-known/apple-app-site-association)可以查看一个实际示例。

### 4.2 Android 上的关联文件：assetlinks.json

Android 使用 `assetlinks.json`
文件，与其 iOS 对应文件一样，对于 WebAuthn 和通行密钥需要进行特定的配置。

```json
[
    {
        "relation": [
            "delegate_permission/common.handle_all_urls",
            "delegate_permission/common.get_login_creds"
        ],
        "target": {
            "namespace": "android_app",
            "package_name": "com.corbado.passkeys.pub",
            "sha256_cert_fingerprints": [
                "F8:90:4E:9A:99:01:71:75:25:38:D5:36:16:2D:B3:65:EB:41:51:D4:53:9A:72:BC:4B:56:C5:16:43:62:E2:C0"
            ]
        }
    }
]
```

您需要设置 relation 值 `delegate_permission/common.handle_all_urls` 和
`delegate_permission/common.get_login_creds`。此外，您需要添加您的包名称和签名证书的 SHA-256 指纹。

要允许在原生应用和 Web 应用之间共享通行密钥，您需要添加两个条目。一个针对 namespace
web，另一个针对 namespace android_app。

[在此处](https://pro-6244024196016258271.frontendapi.cloud.corbado.io/.well-known/assetlinks.json)可以查看一个实际示例。

要让 Android 应用正常运行，assetlinks.json 文件必须存储在依赖方 ID 的 `/.well-known`
目录下 `https://<依赖方 ID>/.well-known/assetlinks.json` - 因此与 iOS 非常相似）。

请查看这篇博文，以了解在原生 Android / iOS 应用和 Web 应用之间共享通行密钥的示例实施。

## 5. 在原生应用和 Web 应用之间建立信任

### 5.1 iOS

在 iOS 应用和 Web 应用之间建立信任的过程如下：

1. iOS 应用开发者必须指定一个他希望与该原生应用相关联的域名列表。这些域名被硬编码在 iOS 应用的授权中，例如：

- webcredentials:auth.Corbado.com
- webcredentials:\*.Corbado.com

2. 每次安装或更新 iOS 应用时，iOS 都会为 iOS 应用授权列表中的每个条目下载 apple-app-site-association 文件。

3. 当在 iOS 应用内部创建了凭证（例如通行密钥）时，iOS 应用会通过检查以下两个方面来验证依赖方服务器的域名是否与该 iOS 应用相关联：

- 设备上是否存在针对此依赖方服务器域名的 `apple-app-site-association` 文件？
- 该 iOS 应用是否列在该 `apple-app-site-association` 文件中？

当且仅当这两个问题都能给出肯定的回答时，才能在 iOS 应用中创建通行密钥。

### 5.2 Android

在 Android 应用和 Web 应用之间建立信任的过程如下：

1. Android 应用开发者必须指定一个他希望与该 Android 应用相关联的域名列表。在 assetlinks.json 文件中，这些域名被存储为带有 namespace
   web 的 target。要声明 Android 应用和 Web 应用共享凭证，需要指定
   `delegate_permission/common.get_login_creds`。在[此处](https://developer.android.com/training/sign-in/passkeys#add-support-dal)可了解详细信息。

2. 如果在 Android 应用内部创建了通行密钥，Android 应用会通过检查 `assetlinks.json`
   来验证依赖方 ID 是否与 Android 应用相关联：

- 是否在 `https://<依赖方 ID>./well-known/assetlinks.json` 处存在针对此依赖方 ID 的
  `assetlinks.json` 文件
- Android 应用是否被正确定义为 target。

3. 当且仅当这两个问题都能给出肯定的回答时，才能在 Android 应用中创建通行密钥。

下图并排比较了这些建立信任的过程。

## 6. Android、iOS 和 Flutter 设置概览

在接下来的内容中，我们将详细概述为原生应用正确设置通行密钥身份验证所需的各种设置。

| 功能                                      | iOS                                                                                                                                                                   | Android                                                                                                                                                                                                                                                                                                                    |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 官方关于原生通行密钥身份验证的实施指导    | 苹果开发者 (Apple Developer)                                                                                                                                          | 谷歌开发者 (Google Developer)                                                                                                                                                                                                                                                                                              |
| 允许与 Web 应用共享通行密钥               | 是，通过 apple-app-site-association 文件                                                                                                                              | 是，通过 assetlinks.json                                                                                                                                                                                                                                                                                                   |
| 关联文件的位置                            | [https://acme.com/.well-known/apple-app-site-association](https://acme.com/.well-known/apple-app-site-association)                                                    | [https://acme.com/.well-known/assetlinks.json](https://acme.com/.well-known/assetlinks.json)                                                                                                                                                                                                                               |
| 文件已缓存                                | 是（自 iOS 14 起），初始同步最多可能需要 24 小时                                                                                                                      | 是（由 Play Services 提供缓存）                                                                                                                                                                                                                                                                                            |
| 可能被绕过                                | 是，使用 alternate mode 部分                                                                                                                                          | 否                                                                                                                                                                                                                                                                                                                         |
| 可进行测试的工具                          | apple-app-site-association 测试                                                                                                                                       | assetlinks.json 测试                                                                                                                                                                                                                                                                                                       |
| 带示例的应用标识符                        | `<Application Identifier Prefix>.<Bundle Identifier>`，例如 `T84QZS65DQ.com.facebook.Messenger`                                                                       | SHA256 指纹，例如 `E3:F9:E1:E0:CF:99:D0:E5:6A:05:5B:A6: 5E:24:1B:33:99: 7F:CE:A5:24:32: 6B:0C:DD:6E:C1:32: 7E:D0:FD:C1`                                                                                                                                                                                                    |
| 哪里能找到应用标识符                      | 可以在 developer.apple.com 上的开发者帐户中找到团队应用标识符前缀（Team Application Identifier Prefix），而捆绑包标识符（Bundle Identifier）是 XCode 项目中确切的名称 | 已经上传时：Google Play Console &gt; Release management &gt; App signing；本地：`keytool -list -v -keystore <keystore path> -alias <key alias> -storepass <store password> -keypass <key password>`                                                                                                                        |
| 将应用链接至 Web 的部分的名称             | applinks（通行密钥不需要）                                                                                                                                            | `delegate_permission/common.handle_all_urls`（通行密钥必填）                                                                                                                                                                                                                                                               |
| 允许在应用 / Web 之间共享凭证的部分的名称 | webcredentials                                                                                                                                                        | `delegate_permission/common.get_login_creds`                                                                                                                                                                                                                                                                               |
| 所有关联文件的注册表                      | 在 XCode 开发环境中启用并添加关联域名（设置属性以生成授权文件）                                                                                                       | 使用 Credential Manager API 时，通行密钥不需要在 manifest 中注册 assetlinks.json（但密码需要）。不使用 Credential Manager API 时，您需要在 AndroidManifest.xml 的特定 activity 中用 `<data>`-entry 列出主机名（Android 应用源代码的一部分）。需要设置 `<intent-filter android:autoVerify="true">` 且使其 autoVerify=true。 |

对于 [Flutter](https://www.corbado.com/blog/flutter-passkeys-package)
来说，适用 Android 或 iOS 各自的规则。唯一特定于 [Flutter](https://www.corbado.com/blog/flutter-passkeys-package)
的设置是关联文件的注册，您应该在其中添加：

- 针对 Android：[在 AndroidManifest.xml 中开启 flutter_deeplinking_enabled](https://docs.flutter.dev/cookbook/navigation/set-up-app-links)
- 针对 iOS：[在 Info.plist 中添加 FlutterDeepLinkingEnabled true](https://docs.flutter.dev/cookbook/navigation/set-up-universal-links)

## 7. 有效及无效的依赖方 ID 与关联文件示例

由于我们亲身经历了处理不同级别的（子）域名和 CNAME 的依赖方 ID 可能是一项相当具有挑战性的任务，因此我们将展示四个不同的示例并解释它们有效或无效的原因及方式。

请注意，CNAME 表格行并不是通行密钥身份验证所必需的，仅是我们想要补充的研究结果。

### 7.1 示例 1：依赖方是根域名

| **依赖方 ID**                             | corbado.com                                                                                                              |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **授权（仅适用于 iOS）**                  | webcredentials:corbado.com                                                                                               |
| **apple-app-site-association 文件的位置** | [https://corbado.com/.well-known/apple-app-site-association](https://corbado.com/.well-known/apple-app-site-association) |
| **assetlinks.json 文件的位置**            | [https://corbado.com/.well-known/assetlinks.json](https://corbado.com/.well-known/assetlinks.json)                       |
| **CNAME**                                 | 不适用                                                                                                                   |

在这个例子中，在安装 / 更新原生应用时，可以没有任何问题地下载 Corbado.com 的
`apple-app-site-association` / `assetlinks.json`
文件，因为该文件位于与依赖方 ID 相同的位置。

**可以为该依赖方 ID 创建通行密钥。**

### 7.2 示例 2：依赖方是子域名且设置了 CNAME

| 依赖方 ID                                 | auth.corbado.com                                                                                                                         |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **授权（仅适用于 iOS）**                  | webcredentials:auth.corbado.com                                                                                                          |
| **apple-app-site-association 文件的位置** | [https://pro-123.passkeys.eu/.well-known/apple-app-site-association](https://pro-123.passkeys.eu/.well-known/apple-app-site-association) |
| **assetlinks.json 文件的位置**            | [https://pro-123.passkeys.eu/.well-known/assetlinks.json](https://pro-123.passkeys.eu/.well-known/assetlinks.json)                       |
| **CNAME**                                 | auth.corbado.com =&gt; pro-123.passkeys.eu                                                                                               |

在这个例子中，在安装 / 更新原生应用时，可以毫无问题地下载 auth.corbado.com 的
`apple-app-site-association` / `assetlinks.json`
文件，由于 CNAME 指向从依赖方 ID 到存储的位置，所以文件位于依赖方 ID 所对应的位置。

**可以为该依赖方 ID 创建通行密钥。**

### 7.3 示例 3：依赖方是根域名且设置了 CNAME

| 依赖方 ID                                 | corbado.com                                                                                                                              |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **授权（仅适用于 iOS）**                  | webcredentials:corbado.com; webcredentials:auth.corbado.com                                                                              |
| **apple-app-site-association 文件的位置** | [https://pro-123.passkeys.eu/.well-known/apple-app-site-association](https://pro-123.passkeys.eu/.well-known/apple-app-site-association) |
| **assetlinks.json 文件的位置**            | [https://pro-123.passkeys.eu/.well-known/assetlinks.json](https://pro-123.passkeys.eu/.well-known/assetlinks.json)                       |
| **CNAME**                                 | auth.corbado.com =&gt; pro-123.passkeys.eu                                                                                               |

在这个例子中，在安装 / 更新原生应用时，可以毫无问题地下载 auth.corbado.com 的
`apple-app-site-association` / `assetlinks.json` 文件，因为通过 CNAME 的作用，文件位于
`auth.corbado.com` 预期它所在的位置。

但是：在安装 / 更新原生应用时，不能下载 corbado.com 的 `apple-site-association-file` /
`assetlinks.json`，因为该文件不在预期的
`https://corbado.com/.well-known/apple-app-site-association` /
`https://corbado.com/.well-known/assetlinks.json` 位置，而且也没有 CNAME 指向它。

**无法为该依赖方 ID 创建通行密钥。**

### 7.4 示例 4：依赖方是子域名且设置了通配符授权

| 依赖方 ID                                 | auth.corbado.com                                                                                                         |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **授权（仅适用于 iOS）**                  | webcredentials:\*.corbado.com                                                                                            |
| **apple-app-site-association 文件的位置** | [https://corbado.com/.well-known/apple-app-site-association](https://corbado.com/.well-known/apple-app-site-association) |
| **assetlinks.json 文件的位置**            | [https://corbado.com/.well-known/assetlinks.json](https://corbado.com/.well-known/assetlinks.json)                       |
| **CNAME**                                 | 不适用                                                                                                                   |

在这个例子中，在安装 / 更新原生应用时，可以毫无问题地下载 corbado.com 的
`apple-app-site-association`
文件，因为文件在它预期所在的位置，并且 webcredentials 授权（`*.corbado.com`）与依赖方 ID（`auth.corbado.com`）相匹配。请注意，此示例在 Android 上无效，因为 Android 不支持（通配符）授权之类的功能。通常，不建议采用这种方式定义依赖方 ID。

**可以为该依赖方 ID 创建通行密钥。**

## 8. 常见的依赖方 ID 错误以及如何避免

### 8.1 将依赖方 ID 从子域名更改为根域名

**错误：**

您开始进行开发并将一个子域名（例如
`login.acme.com`）定义为您的依赖方 ID。首批用户已针对该依赖方 ID 创建了通行密钥。然后，您发现在另一个子域名（例如
`app.acme.com`）上进行身份验证时也需要这些通行密钥。由于新子域名上用户的源与该依赖方 ID 不匹配，用户无法通过通行密钥登录。此时，在 WebAuthn 设置中将依赖方 ID 更改为
`acme.com`
会导致所有现有的通行密钥失效，因为新的源与存储在现有通行密钥中的依赖方 ID 不匹配。

**解决方案：**

在定义您的依赖方 ID 时请在最初仔细检查，因为该操作在很大程度上是不可更改的。如果您不确定并想满足未来的需求（也就是未来其他子域名可能也需要此通行密钥进行身份验证），除非该域名位于[公共后缀列表](https://publicsuffix.org/learn/)中，否则我们建议使用根域名（例如 acme.com）作为依赖方 ID。

### 8.2 原生应用和 Web 应用采用不同的依赖方 ID

**错误：**

您正在同时开发一个原生应用和一个 Web 应用。为了加快进度，您使用了两台不同的 WebAuthn 服务器（为原生应用和 Web 应用配置了不同的依赖方 ID）。随着您的用户创建了第一批通行密钥，各自的依赖方 ID 就存储到了通行密钥中。此时就不再支持使用同一个通行密钥进行跨设备 / 跨平台登录了，例如在 Web 应用中创建的通行密钥，尝试在原生应用中进行登录时是行不通的。即使将这两台 WebAuthn 服务器合并，也会导致在旧 WebAuthn 服务器（旧依赖方 ID）中注册的那些通行密钥被废弃，您的用户也就无法再用这些通行密钥登录了。

**解决方案：**

如果您有多个应用程序（例如一个 Web 应用和一个原生应用），请始终只使用一台 WebAuthn 服务器，并且为您所有的应用只定义一个依赖方 ID。这几个应用间的关联连接可以通过上述步骤来完成。

### 8.3 关联文件无效或无法访问

**错误：**

您开始开发您的应用程序，配置了关联文件，并将它们部署到您的服务器。但不知什么原因，您还是收到错误提示，并且找不到根本原因。

**解决方案：**

报错的潜在原因可能在于关联文件格式错误或无法访问。在将任何关联文件部署到服务器之前，我们强烈建议通过为
[iOS](https://www.corbado.com/blog/how-to-enable-passkeys-ios)
和 Android 提供的工具来检查关联文件的有效性和可访问性（通常这些文件可能位于[强大的 VPN](https://cybernews.com/best-vpn/most-secure-vpns/)
或防火墙后面，这阻碍了 Apple 和 Google 的爬虫正常访问）。

### 8.4 apple-app-site-association 文件尚未被 Apple CDN 缓存

**错误：**

您已将 apple-app-site-association 文件部署到了服务器，并想马上在测试设备上开始创建通行密钥。可不知什么原因，您无法创建通行密钥且收到报错信息。

**解决方案：**

这些报错信息的背后原因是，iOS 设备会下载 `apple-app-site-association`
文件来验证依赖方。而为达到这一目的，iOS 设备并不会直接向您的服务器发送请求，而是使用一个 CDN 替代。该设备和 CDN 在成功检索到
`apple-app-site-association` 文件后都会对其进行缓存。由于存在这种缓存功能，您的
`apple-app-site-association`
文件里的最新更改无法直接在您的应用中体现。CDN 最多可能需要 24 小时才能完成对
`apple-app-site-association` 文件的缓存。为了在开发过程中避开此项限制，您可以将
`?mode=developer` 追加到依赖方 ID 后，从而彻底停用缓存（例如，此时依赖方 ID 将变成
`acme.com?mode=developer`）。

### 8.5 Android 模拟器与 API 版本不兼容

**错误：**

您开始开发一个 Android 应用，并想在 Android 模拟器上对其进行测试。不知出于何种原因，即使您已经妥善设置了 Android 模拟器并且其他应用似乎也能在上面流畅运行，您还是收到了错误消息。

**解决方案：**

在测试通行密钥应用程序时，Android 版本、Play
Store 的支持情况和 API 版本发挥着主要的作用。另外，您必须已登录一个 Google 账户。有关详细信息，请参阅我们的疑难解答部分。

## 9. 建议

### 9.1 通用建议

我们的整体建议是：根据您的应用程序生态结构和相关需求，谨慎地选择您的依赖方 ID。我们在下方汇总了大多数常见的使用场景，但我们一般性的建议是，您应该**尽量选择根域名来作为依赖方 ID**，并以此方式配置身份验证。在 Corbado 中，我们也已经提前为您预配了这种方式（这是我们致力于为所有技术配置提供无缝通行密钥身份验证策略的一部分。您可以直接将您的根域名作为依赖方 ID 来使用我们的 UI 组件和 SDK）。

| 案例                                                                                                                                                                                                                                                                                                                     | 建议                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **A) 您拥有一个根域名：**<br/><br/>例如：acme.com<br/><br/>所有应用程序及身份验证均在该根域名或其子域名上运行                                                                                                                                                                                                            | ✔️ 选择根域名作为您的依赖方 ID，因为这不会给 Web 应用或原生应用带来任何问题。                                                                                                                                                                                                                                                                                                                                                                                                                      |
| **B) 您有多个根域名：**<br/><br/>例如：kayak.com，kayak.co.uk，kayak.de<br/><br/>您通过不同的国际顶级域名为您的用户提供服务。Kayak.com 针对美国，而 kayak.co.uk 针对英国，或者您拥有截然不同的各种根域名，并且应该允许同一批用户用相同的通行密钥进行登录。                                                               | ⚠️ 在您的 Web 应用上，如果想要共享通行密钥，需通过 `.well-known/webauthn` 配置相关源请求（Related Origin Requests），从而允许特定源能使用一个共同的 RP ID（浏览器的支持度各不相同；请确认兼容性）。或者，您可以选择一个共同的用于身份验证的根域名。<br/><br/>✔️ 只要您能够掌控根关联文件，您可以将您的原生应用连接到任何数量的根域名上。<br/><br/>❌ 如果您以后想要迁移到另一个根域名来托管您的网站，您就将无法再使用已经创建好的那些通行密钥，因为您将不得不重塑品牌形象并变更域名（依赖方 ID）。 |
| **C) 您目前还没有根域名，您仅是在后端或者是某个公共的子域名上运行。某些情况可能引发这个问题：**<br/><br/>1. 您的工作所基于的是一个免费使用的子域名，这里的根域名并不受您的控制（该根域名被列在了 [https://publicsuffix.org/](https://publicsuffix.org/) 中），比如 CDN 的 URL<br/><br/>2. 您的工作涉及的是一个原生应用。 | ❌ 在公共的子域名上，您没有权限在属于根域名的根层级上掌控那些关联文件。因此，通行密钥将无法原生运作。<br/><br/>⚠️ 解决该问题的唯一办法就是针对某些服务转为付费计划，在此计划内，您可以去定义一个 CNAME，或者您亲自获取一个专属您的定制根域名。                                                                                                                                                                                                                                                     |

### 9.2 在使用 Corbado 时的相关建议

我们在下面提供了一个极为详细的决策树，在您使用 Corbado 作为您的通行密钥解决方案时，此决策树应能协助您决定正确的依赖方 ID，并说明您应该以怎样的方式处理 / 托管关联文件。

首要的决策取决于您身处开发环境还是生产环境。您的下一个决策层级必须基于您的应用程序生态结构：您是仅仅只有一款原生应用，还是同时拥有原生应用及 Web 应用。

#### A) 开发环境

对于开发环境，我们假定您希望尽快开始开发以及测试。若之后您打算上线，依赖方 ID 是可以修改的。

##### A1) 仅原生

- 将依赖方 ID 设置为 `pro-XXX.frontendapi.cloud.corbado.io`（默认值）
- Corbado 会替您托管关联文件
- 对您而言没有任何涉及 DNS 的待办事项

##### A2) 原生应用与 Web 应用

同时顺利测试 Web 应用和原生应用并不那么容易

**选项 1：**

要么您设置依赖方 ID =
`pro-XXX.frontendapi.cloud.corbado.io`（原生应用能运行），要么您设置依赖方 ID =
`localhost`（Web 应用能运行）

**选项 2：**

要想让原生应用和 Web 应用能同时运行的唯一办法就是使用本地的反向代理（这多少算是一种应急变通的解决方案）：

- 设置依赖方 ID = `acme-dev.com`
- 将来自 `acme-dev.com` 的 CNAME 设置为 =&gt; `pro-XXX.frontendapi.cloud.corbado.io`
- 添加一个本地的 `/etc/hosts` 条目：`localhost acme-dev.com`
- 为 `acme-dev.com` 添加带规则的反向代理（nginx），规则为 =&gt;
  `localhost:3000`（作为一个例子）

#### B) 生产环境

在生产环境中，您必须要判断，使用一个子域名作为依赖方 ID（例如
`auth.acme.com`）能否满足您的要求，或者您是否期望能有一个根域名来充当依赖方 ID（例如
`acme.com`）

##### B1) 将子域名作为依赖方 ID

###### B1.1) 仅原生

- 设置依赖方 ID = `auth.acme.com`
- Corbado 会替您托管关联文件
- 将来自 `auth.acme.com` 的 CNAME 设置为 =&gt; `pro-XXX.frontendapi.cloud.corbado.io`

###### B1.2) 原生应用与 Web 应用

- 设置依赖方 ID = `auth.acme.com`
- Corbado 会替您托管关联文件
- 将来自 `auth.acme.com` 的 CNAME 设置为 =&gt;
  `pro-XXX.frontendapi.cloud.corbado.io`（如果您要使用 Corbado 的会话管理机制，要让 Cookie 正常工作也需要设置）

##### B2) 将根域名作为依赖方 ID

###### B2.1) 仅原生

- 设置依赖方 ID = `acme.com`
- 在您自己的服务器上的 `acme.com/.well-known/<关联文件>` 自行托管关联文件
- 对您而言没有任何涉及 DNS 的待办事项

###### B2.2) 原生应用与 Web 应用

- 设置依赖方 ID = `acme.com`
- 在您自己的服务器上的 `acme.com/.well-known/<关联文件>` 自行托管关联文件
- 如果您使用了 Corbado 的会话管理机制，您就需要将 CNAME 设置为 `auth.acme.com` =&gt;
  `pro-XXX.frontendapi.cloud.corbado.io`，以使 Cookie 能正常工作（这个 CNAME 的作用仅仅是为了会话管理，并非是为了依赖方 ID）

接下来的这棵决策树将为您概括所有的配置路径。

## 10. 结论

依赖方 ID 是 WebAuthn 与基于通行密钥的身份验证方式的基石，能带来强大的抗网络钓鱼能力。恰当配置依赖方 ID、吃透域名匹配的复杂原理并确保原生应用准确无误地部署，这极其关键。本篇博文旨在向您展示如何才能设置无误，并指导您在面对不同的失误情况时该如何应对。当您的 rpID 处于稳固的配置状态时，请专注于优化您的通行密钥创建流程与通行密钥登录流程，去推动真正的采用。如果要深入探究原生应用设置通行密钥的真知灼见，我们建议您阅读这篇关于在
[Flutter](https://www.corbado.com/blog/flutter-passkeys-package) 框架中应用通行密钥的内容。

如果您抱有更多的疑问，或急需他人的协助，请毫不犹豫地[联系我们](https://bit.ly/passkeys-community)。

## 常见问题解答

### 依赖方 ID 如何在通行密钥身份验证中防止网络钓鱼？

身份验证器会将浏览器的实际源与通行密钥中存储的依赖方 ID 进行比较。如果攻击者在不同的域名上托管假冒网站，即使伪造的质询声称有合法的 RP
ID，源的不匹配也会导致身份验证自动失败。这种绑定意味着在 paypal.com 上注册的通行密钥不能在像 paybal.com 这样看似相似的域名上使用。

### 如果在用户已经注册通行密钥后我更改了 WebAuthn 依赖方 ID 会发生什么？

更改 RP ID 会使所有现有的通行密钥失效，因为存储在每个凭证中的 RP
ID 将不再与新值匹配。唯一的例外是新 RP ID 是旧 RP ID 的子域名，或者通过
`.well-known/webauthn` 配置了相关源请求（Related Origin
Requests）。请从一开始就选择根域名作为 RP ID，以避免这种不可逆转的问题。

### 为什么在部署了 apple-app-site-association 文件后我的 iOS 通行密钥没有立即生效？

iOS 不会直接从您的服务器获取 apple-app-site-association 文件。它使用 Apple 的 CDN，缓存新部署或更新的文件可能需要长达 24 小时的时间。在开发过程中，可以将
`?mode=developer` 追加到依赖方 ID 后以完全绕过缓存。

### 针对通行密钥，我应该使用子域名还是根域名作为我的依赖方 ID？

推荐使用根域名（例如 acme.com），因为在任何子域名上创建的通行密钥都可以在该根域名的所有子域名上进行身份验证。子域名的 RP
ID 将通行密钥的使用限制在该子域名及其子集，这可能会在以后破坏跨子域名的流程。如果您有多个根域名（例如 acme.com 和 acme.co.uk），请通过
`.well-known/webauthn` 配置相关源请求（Related Origin
Requests）以允许跨根域名重复使用通行密钥。
