OpenID Connect Discovery 1.0 深度解析:OP 的“自我介绍”和动态配置检索

发布: (2026年3月7日 GMT+8 16:50)
10 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的完整文本内容,我将按照要求保留源链接、格式和技术术语,仅翻译正文部分。谢谢!

Introduction

您可能每天都在使用 OIDC(OpenAI Connect) 将 Google 登录或其他身份验证流程集成到您的应用程序中。
在这样做时,您是否曾经只在库的初始化代码中设置

issuer: "https://accounts.google.com"

就能自动解析 授权端点令牌端点,甚至是公钥的位置(JWKS)?

  • “仅提供 Issuer URL 为什么就能揭示所有端点?”
  • “它如何在不产生停机的情况下跟随公钥(JWKS)轮换?”
  • “首先,它是如何从类似电子邮件的 ID(如 alice@example.com)识别出要进行身份验证的提供者的?”

这些问题的答案是 OpenID Connect Discovery 1.0

在过去的 OAuth 2.0 时代,开发者通常阅读文档并 硬编码 授权服务器的每个端点 URL(例如 /authorize/token)到客户端。
这种做法在提供者更改 URL 或轮换公钥时需要客户端进行更改——显然不可扩展。

OIDC Discovery 1.0 是一种标准化的 机制,供客户端动态发现并检索 OpenID Provider(OP)的配置信息(元数据)

在本文中,我们将基于规范深入探讨 OIDC Discovery 的两个阶段:

阶段目标
Issuer Discovery(阶段 1)根据用户提供的标识符(例如电子邮件地址)发现 是应当对用户进行身份验证的 OpenID Provider(Issuer)。如果已经知道 Issuer(例如点击 “使用 Google 登录”),此步骤是可选的,可直接跳过。
Provider Configuration(阶段 2)向已识别的 Issuer 查询其配置元数据——“你的授权端点在哪里?” “你的公钥(JWKS)在哪里?”

何时需要 Issuer Discovery?

如果您的应用专门用于 Google 登录,Issuer 显然是 https://accounts.google.com
但如果是企业 SaaS,需要 根据用户的电子邮件域@company.com)动态切换目标 IdP,则 RFC 7033 WebFinger 就派上用场了。

标准化用户提供的标识符

用户输入的值可以是:

  • 一个类似 alice@example.com电子邮件地址
  • 一个类似 https://example.com/aliceURL

OIDC Discovery 定义了严格的标准化步骤,以唯一确定:

  • Host – 要联系的服务器
  • Resource – 通过 WebFinger 查询的标识符

标准化规则

条件标准化形式
无方案 且包含 @(例如 joe@example.com解释为 acct: 方案 → acct:joe@example.com
无方案包含 @(例如 example.comexample.com:8080解释为 https://https://example.com
显式方案 (https://, acct:, …)保持原样,不作进一步更改
存在片段 (#…)完全移除片段

第 1 阶段 – 通过 WebFinger 发现 Issuer

假设用户输入了 joe@example.com,其规范化后为 acct:joe@example.com

  1. 识别主机 – 提取 authority 部分(example.com)。
  2. 指定资源 – 使用完整的规范化 URI(acct:joe@example.com)作为 resource 查询参数。
  3. 调用 WebFinger 端点 – 对提取的主机上的 /.well-known/webfinger 执行 HTTP GET 请求。
GET /.well-known/webfinger?resource=acct%3Ajoe%40example.com&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer HTTP/1.1
Host: example.com
  • resource – 要查询的 URL 编码标识符。
  • rel – 固定值 http://openid.net/specs/connect/1.0/issuer,表示“我正在请求 OIDC Issuer 信息”。

示例响应(JRD – JSON 资源描述符)

HTTP/1.1 200 OK
Content-Type: application/jrd+json

{
  "subject": "acct:joe@example.com",
  "links": [
    {
      "rel": "http://openid.net/specs/connect/1.0/issuer",
      "href": "https://server.example.com"
    }
  ]
}

href 值(https://server.example.com)是客户端将在下一阶段使用的 Issuer URL

第 2 阶段 – 提供者配置

在已知 Issuer 后,客户端会检索 OP 的元数据。
OIDC 发现规范要求 OP 在一个路径下公开 JSON 元数据,该路径通过在 Issuer URL 后追加 /.well-known/openid-configuration 形成。

GET /.well-known/openid-configuration HTTP/1.1
Host: server.example.com

⚠️ 常见陷阱 – Issuer 包含路径

如果 Issuer 包含路径(例如 https://example.com/tenant-1):

  1. 移除任何尾随斜杠
  2. 直接在路径后追加 /.well-known/openid-configuration

得到的 URL:

https://example.com/tenant-1/.well-known/openid-configuration

不要错误地将配置文件放在域根目录下(https://example.com/.well-known/openid-configuration),因为许多实现都会这么做。

示例 OP 元数据响应

HTTP/1.1 200 OK
Content-Type: application/json

{
  "issuer": "https://server.example.com",
  "authorization_endpoint": "https://server.example.com/oauth2/v1/authorize",
  "token_endpoint": "https://server.example.com/oauth2/v1/token",
  "userinfo_endpoint": "https://server.example.com/oauth2/v1/userinfo",
  "jwks_uri": "https://server.example.com/oauth2/v1/keys",
  "registration_endpoint": "https://server.example.com/oauth2/v1/register",
  "scopes_supported": ["openid", "profile", "email"],
  "response_types_supported": ["code", "id_token", "token id_token"],
  "grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
  "subject_types_supported": ["public", "pairwise"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "claims_supported": ["sub", "iss", "aud", "exp", "iat", "email", "email_verified", "name"]
}

JSON 对象(OP 元数据)列出了:

  • 端点authorization_endpointtoken_endpointuserinfo_endpointjwks_uri,…)
  • 支持的功能scopes_supportedgrant_types_supportedclaims_supported 等)

有了这些元数据,客户端库可以自动:

  • 将用户引导至正确的授权端点
  • 在适当的令牌端点交换代码
  • 从 JWKS URI 获取公钥(透明地处理密钥轮换)

回顾

  1. Issuer Discovery (可选) – 使用 WebFinger 将用户提供的标识符(电子邮件、URL 等)转换为 Issuer URL。
  2. Provider Configuration – 获取 Issuer 的 /.well-known/openid-configuration 文档,以获取所有必需的端点和功能。

这个两阶段的发现过程消除了硬编码,支持多租户场景,并确保无缝的密钥轮换——全部在不中断服务的情况下完成。

OpenID Connect 提供者配置(发现)

{
  "authorization_endpoint": "https://server.example.com/connect/authorize",
  "token_endpoint": "https://server.example.com/connect/token",
  "userinfo_endpoint": "https://server.example.com/connect/userinfo",
  "jwks_uri": "https://server.example.com/jwks.json",
  "response_types_supported": [
    "code",
    "id_token",
    "id_token token"
  ],
  "subject_types_supported": [
    "public",
    "pairwise"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256",
    "ES256"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_basic",
    "private_key_jwt"
  ],
  "scopes_supported": [
    "openid",
    "profile",
    "email"
  ],
  "claims_supported": [
    "sub",
    "iss",
    "name",
    "email"
  ],
  "registration_endpoint": "https://server.example.com/connect/register"
}

参数概览

参数名称必需 / 可选描述
issuerREQUIREDOP的发行者标识符。用于TLS检查以及在ID Token中验证iss声明。
authorization_endpointREQUIRED用户被重定向进行身份验证的端点。
token_endpointREQUIRED*用于将授权码兑换为令牌的端点。除非OP仅支持隐式流。
jwks_uriREQUIRED公开密钥(JWK集合)所在的URL,用于验证ID Token的签名。
response_types_supportedREQUIREDOP支持的OIDC认证流(例如codeid_tokenid_token token)。
subject_types_supportedREQUIREDsub标识符的类型——public(在所有RP之间相同)或pairwise(对每个RP唯一)。
id_token_signing_alg_values_supportedREQUIREDOP可用于ID Token的签名算法。RS256 必须包含。
token_endpoint_auth_methods_supportedOPTIONAL在令牌端点接受的客户端认证方法(例如client_secret_basicprivate_key_jwt)。
scopes_supportedRECOMMENDEDOP支持的作用域列表。openid作用域应当存在。
claims_supportedRECOMMENDEDOP可以返回的声明列表(例如nameemailsubiss)。
registration_endpointRECOMMENDED动态客户端注册的端点。

深入探讨:jwks_uri

  • 为何重要jwks_uri 是 OP 公钥的唯一可信来源。通过获取该 URL,依赖方(RP)可以验证收到的每个 ID Token 的签名。
  • 密钥轮换 – RP 应检查传入 ID Token 的 kid(密钥标识)头部。如果本地缓存中缺少对应的密钥,RP 必须重新从 jwks_uri 拉取 JWKS。这使得在不中断服务的情况下实现无缝密钥轮换。
  • 安全考虑
    • 冒充攻击:如果攻击者能够让 RP 下载恶意 JWKS,则伪造的 ID Token 将变得可行。

    • 发现规范强制执行严格检查:

      “返回的 issuer必须 与用作 /.well-known/openid-configuration 前缀的 Issuer URL 完全相同。”(OIDC Discovery §4.3)

      “实现 必须 支持 TLS。”(OIDC Discovery §7.1)

    • 所有通信(WebFinger 和 Provider Configuration)必须通过 HTTPS 进行,RP 必须验证服务器的 TLS 证书(RFC 6125)。明文传输会让中间人篡改 jwks_uri 为攻击者控制的服务器。

OIDC Discovery 1.0 的关键要点

  1. 不使用硬编码 URL – 使用 /.well-known/openid-configuration 让 RP 在运行时发现 OP 的端点和功能。
  2. WebFinger 集成 – 像用户邮箱这样的标识符可以在发现阶段 1 中解析到正确的 Issuer。
  3. 自动化密钥轮换 – 动态获取 JWKS 实现了稳健、无缝的安全运维。

下次使用 OIDC 库时,想象一下对 /.well-known/openid-configuration 的隐藏请求,这正是上述所有功能的驱动核心。

参考文献

  • OpenID Connect Discovery 1.0 – 用于提供者元数据的核心规范。
  • RFC 7033 – WebFinger – 用于发现资源信息的机制(例如电子邮件地址)。
  • RFC 5785 – Well‑Known URIs – 定义用于发现端点的 /.well-known/ 命名空间。
0 浏览
Back to Blog

相关文章

阅读更多 »

AI、人类与我们打破的循环

🌅 经验的回响 — 站在地平线 曾经有一段时间,混沌塑造了我。但当我真正选择了自己——真正选择了自己——一切都改变了。我…