{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreig7twmxozm7sbnlaxp2p3a7x43egdzbdeikxhexhbi2wsrvspbyqq",
"uri": "at://did:plc:hbxmxjoelm7r62sccrote3vf/app.bsky.feed.post/3lgtnzjxtgge2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreibvo4tnqxilu35zvu3ilha5pfs3gl6juh3prk34eyditow43poswa"
},
"mimeType": "image/png",
"size": 13415
},
"path": "/2025/auth0-oidc-sso/",
"publishedAt": "2026-06-05T09:53:42.127Z",
"site": "https://blog.outv.im",
"tags": [
"[1]",
"Nextcloud 的官方 OIDC 登录插件",
"配置应用通过 Auth0 登录",
"给 Auth0 添加社交平台(例如 GitHub)登录支持",
"Keycloak",
"要加钱",
"太多啦",
"被 Okta 收购了",
"Okta",
"Cisco Duo",
"PingIdentity",
"OneLogin",
"在 Entra ID 创建一个 application",
"$LAPSUS 宣称成功入侵 Okta 系统",
"截图",
"Okta 的官方声明",
"Okta 发布公告",
"Cloudflare 称",
"简单的文章",
"复杂的文章",
"Microsoft 的文档",
"OIDC(以及其它 OAuth 流程)的 playground",
"OpenID Connect Discovery",
"https://login.microsoftonline.com/contoso.com/.well-known/openid-configuration",
"https://login.microsoftonline.com/6babcaad-604b-40ac-a9d7-9fd97c0b779f/oauth2/authorize",
"https://login.microsoftonline.com/6babcaad-604b-40ac-a9d7-9fd97c0b779f/oauth2/token",
"https://login.microsoftonline.com/common/discovery/keys",
"https://gitea.example.com/user/oauth2/msauth/callback",
"Social Connections",
"不支持禁止通过社交平台注册",
"帐号链接",
"这里",
"↩︎"
],
"textContent": "## 缘起 #\n\n突然想搞一个统一登录系统。一个原因是,托管的各个应用上帐号的密码不一样(废话!),管理起来稍显麻烦;另一个原因是,使用这些应用的时候,需要在每个应用都登录一次,用起来也比较麻烦。\n\n好麻烦,不干啦!働きたくない!不过据说搞个什么 SSO 的能解决这个问题,那么我们也来搞一个吧。\n\n所以,这篇文章的主题是利用 Auth0 这样的 IdP (Identity Provider) 平台,使得用户可以使用同一个帐号在各个 selfhost 的应用上登录。另外,由于登录都通过 Auth0,只要登录了一个应用一次,在 Auth0 的登录过期之前都可以使用 Auth0 的登录 session 直接登录到其它应用,达成所谓的 SSO (Single Sign-On)。\n\n下文中使用「应用」一词表示通过 SSO 登录到的网站(spec 中的 \"Relying Party\",例如 Miniflux),使用 IdP 一词表示提供 SSO 登录功能的服务(spec 中的 \"OpenID Provider\",例如 Auth0)。\n\n## 考虑 #\n\n在配置 SSO 之前,需要决定自己预期的 SSO 行为。在默认配置下,可能会出现在应用中为同一位使用者创建了多个账户,或者为在应用中本不应拥有账户的使用者创建了账户的情况。因此,考虑预期 SSO 行为和系统安全息息相关。\n\n### 安全性 #\n\n在配置一个可以通过 OpenID Connect (OIDC) 使用 IdP 提供的帐号登录的应用的时候,这几件事可能需要仔细地考虑:\n\n#### 用户的注册 #\n\n * IdP 是否应该允许用户注册?对于面向消费者的服务,这一般是肯定的。但对于面向组织内部成员的服务来说,允许用户自行注册通常不是一个好办法;常见的策略是由管理员手工为用户创建 IdP 账户。\n * 在 Auth0 中,可以通过给 Username-Password-Authentication 的 Settings 配置 \"Disable Sign Ups\" 选项配置。\n * 应用要不要允许通过 OIDC 登录的用户在应用上创建账户?许多组织可能会允许通过 OIDC 登录的用户在应用自行创建账户,这样就不必在创建新用户之后手工在各个应用创建此用户的账户。例如,在新员工入职的时候,员工的姓名和邮件地址都被写入 IdP 中;在员工登录 Slack 的时候,Slack 就可以通过 ID token 中的这些信息自动创建员工 Slack 账户,并为其管理 Slack 账户的显示名。\n\n\n\n#### 用户身份的关联 #\n\n应用如何通过 ID token 提供的信息,决定让用户登录到哪个帐号?常见的关联方法大概有这么几种:\n\n * 手工绑定(例如 Miniflux):ID token 中会有一个 `sub` (Subject) claim ,是 IdP 给这个应用提供的用来标识用户的唯一值。只要让已在应用中登录的用户通过 OIDC 登录,记录 ID token 中的这个 `sub` 值,就可以关联应用中的用户和 IdP 中的用户。\n * 通过 email 地址匹配(例如 Gitea):ID token 中经常会有 `email` claim(在 OIDC 流程的 scope 中申请了 `email` scope 的话),写着这个 IdP 中账户的 email 地址。把这个 email 地址和应用账户列表中的 email 字段对比,就可能找到零个、一个或多个可以用来登录的账户。在这种情况下,需要特别注意 IdP 提供的 email 地址是否已被验证属于 IdP 用户[1]。\n * 通过其它 claim 匹配(例如 Nextcloud 的官方 OIDC 登录插件):管理员可以将应用账户的识别信息写入 IdP 的数据库中,再通过让 IdP 在返回的 ID token 中加入此信息作为额外的 claim,来让应用找到需要关联或者登录的应用账户。\n\n\n\n#### 2FA/MFA 放在哪? #\n\n二步验证 (2-Factor Authentication) 或多步验证 (Multi-Factor Authentication) 已经成为现代用户管理系统的必备项。但在 OIDC 中,这个二步验证是放在 IdP 侧还是应用侧(或者都配置?),可能会需要一些考虑:\n\n| 优点 | 缺点\n---|---|---\n放在 IdP 侧 | 配置统一方便\n无需应用兼容\n可以使用 IdP 提供的用户交互验证应用 | 应用对 IdP 完全信任\n放在应用侧 | 即使 IdP 被骇,MFA 依旧可以保护应用帐号安全 | 配置麻烦\n不是所有应用都支持 MFA\nIdP 配置了 MFA 的情况下,应用可能不支持跳过 MFA\n\n### IdP 的角色 #\n\nIdP 可以作为 OpenID Provider,为其它应用提供 OIDC 服务;又因为 IdP 也需要登录用户,它也可以作为 Relying Party,通过另外的 IdP 来登录(例如社交帐号登录)。所以,在配置 IdP 时,需要确定 IdP 担当的角色,它是用户数据的提供者,帮助用户登录到其它应用;还是用户数据的消费者,需要访问其它的 IdP 平台以允许用户登录?两边各举一个例子:\n\n * 当用户通过 Auth0 登录到 NextCloud 时,Auth0 的角色是 OpenID Provider。NextCloud 通过验证和读取 Auth0 提供的 ID token 来决定是否允许用户登录到哪个 NextCloud 账户。\n * 下方的「配置应用通过 Auth0 登录」讲述了配置 IdP 作为 OpenID Provider 的方法。\n * 当用户通过 GitHub 登录到 Auth0 时,Auth0 的角色是 Relying Party。Auth0 通过读取 GitHub 提供的 ID token 来决定提供给用户的 ID token 应该使用 Auth0 哪个账户的信息。\n * 下方的「给 Auth0 添加社交平台(例如 GitHub)登录支持」讲述了配置 IdP 作为 Relying Party 的方法。\n\n\n\n### IDaaS 服务的选择 #\n\n(这里的前提是「如果不自建的话」;打算搭 Keycloak 的话可以跳过此段落。)\n\n简而言之:Azure AD (Entra ID) 是经典玩法,账户系统在 AD/AAD 的可以优先考虑;Auth0 好看功能又多,但 MFA 要加钱;Okta 免费的开发者套餐支持 MFA(例如 Okta 自己的 Okta Verify),但只支持 5 个应用。IdP 护城河算不上深,也有很多小厂商在做,但请深刻考虑对它们的信任,因为密码学并不能阻止它们随意登录到你的系统。\n\n做 IDaaS (Identity as a Service) 的厂商太多啦。Auth0 是最出名的 IDaaS 厂商之一(被 Okta 收购了),其它的大型厂商也有不少:Okta、Cisco Duo、PingIdentity,还有 OneLogin 等等。另外,还有或许和前面几家公司的服务相比要远远更加出名的 Azure Active Directory (Microsoft Entra ID)。通过在 Entra ID 创建一个 application,第三方应用可以非常方便地配置 Microsoft 账户(包括个人账户和公司/学校账户)的社交平台登录支持。\n\n#### 特别地,关于 Okta #\n\nOkta 确实被许多大型跨国企业所信任,但笔者觉得读者需要知道近几年的 Okta 并不安宁:\n\n * 2022 年三月,$LAPSUS 宣称成功入侵 Okta 系统(甚至还有截图!)。Okta 的官方声明说,这是当年一月 Okta 的外包客户支持商 Sitel 雇员的账户被骇,而 Okta 一月就发现了这个问题。对于直到 $LAPSUS 发布入侵截图之后的三月下旬才出面解释的行为,Okta 在声明中也承认拖延公告安全事件是「犯了个错误」。\n * 当年十二月,Okta 发布公告称被 GitHub 通知代码库受到未授权访问。公告提到称代码库泄露不会影响服务的安全性。至于为什么代码库会泄露,Okta 的公告里只字未提。\n * 2023 年十月,Cloudflare 称发现其 Okta 服务被骇。Cloudflare 的文章中提到,Okta 又一次没有尽快向用户披露安全事件,甚至修复漏洞都用了两个多星期。\n\n\n\nOkta, are you Ok?\n\n## 实装 #\n\n### 配置应用通过 Auth0 登录 #\n\n除了 OIDC 之外,另一个常用的方案是 SAML。SAML 较为复杂且过于企业级,本文暂不提及。\n\nOAuth 2.0 是一个授权 (AuthZ, authorization,「确定客户端**是否有权限** 」) 协议,而 OIDC (OpenID Connect) 是基于 OAuth 2.0 之上的认证 (AuthN, authentication,「确定客户端**是谁** 」) 协议。阮一峰有一篇简单的文章和一篇复杂的文章,它们对 OAuth 的解释都不错。Microsoft 的文档也尚且能看。Auth0 还有一个超棒的 OIDC(以及其它 OAuth 流程)的 playground,可以直观地展示 OIDC 的流程。\n\n**简而言之** ,一般的流程是:\n\n 1. 在 IdP 创建 application,获得 Client ID、Client Secret 和 OIDC Auto Discovery URL 等\n 2. 在应用创建 OIDC 登录方式,填入上述值,并获得 Callback URL\n 3. 在 IdP 的 application 填入 Callback URL\n 4. 搞定!\n\n\n\n也就是说,通常有这些必要的信息:\n\n应用端配置(IdP 提供):\n\n名称 | 注释 | 示例\n---|---|---\nClient ID(有时也称作 App ID) | 这是一个用来代表应用的值。虽然经常是 UUID 的格式(例如在 Azure AD),但也有是其它格式的时候。 | ec685f7d-0aac-4585-9ad4-df6bdaf180b6\nClient Secret | 这是应用用来证明自己身份的值。正如名字所说的一样需要保密。 | (略)\nScope | OAuth2 中表示应用所申请权限的值。在 OIDC 中通常是右边这三个。 | `openid email profile`\nOIDC Auto Discovery URL **或** Authorization/Token/Certificate Endpoint | 应用在 OIDC 过程中需要访问的几个端点。两种配置项择一配置即可,应用可能会只支持配置其中一种或均会支持。详情见下文。 |\n\n**关于 OIDC Auto Discovery URL 及 Authorization/Token/Certificate Endpoint** :\n\n在 OIDC 的过程中,应用需要访问 Authorization Endpoint 以使用户验证身份,访问 Token Endpoint 以获取 ID token,以及访问 Certificate Endpoint 以验证 ID token 的有效性。为了方便起见,OIDC 标准中提供了 OpenID Connect Discovery。实现了 Discovery 的 IdP 会提供一个包含上述信息的文档(称作 OpenID Provider Configuration),放置在 `{issuer}/.well-known/openid-configuration`,其中 `{issuer}` 是 token 的 `iss` claim 的值。\n\n在 Auth0,这些 URL 可以在应用设置页最下方 \"Advanced Settings\" 的 \"Endpoints\" 选项卡中找到。它们在那里分别叫做 \"OpenID Configuration\" 和 \"OAuth Authorization URL\"、\"OAuth Token URL\"、\"JSON Web Key Set\"。\n\n这些 URL 的示例:\n\n * Auto Discovery URL: https://login.microsoftonline.com/contoso.com/.well-known/openid-configuration\n * Authorization Endpoint: https://login.microsoftonline.com/6babcaad-604b-40ac-a9d7-9fd97c0b779f/oauth2/authorize (即 Provider Configuration 中的 `authorization_endpoint`)\n * Token Endpoint: https://login.microsoftonline.com/6babcaad-604b-40ac-a9d7-9fd97c0b779f/oauth2/token (即 Provider Configuration 中的 `token_endpoint`)\n * Certificate Endpoint: https://login.microsoftonline.com/common/discovery/keys (即 Provider Configuration 中的 `jwks_uri`)\n\n\n\nIdP 端配置(应用提供):\n\n名称 | 注释 | 示例\n---|---|---\nCallback URL | 这是 IdP 验证用户身份成功后,携带 ID token(或用于兑换 ID Token 的 Authorization Code)跳转回的 URL。这个 URL 通常由应用决定,但需要在 IdP 端配置。IdP 端会验证传入 Authorization Endpoint 的 Callback URL 是否在自己的已知列表中。 | https://gitea.example.com/user/oauth2/msauth/callback\n\n### 给 Auth0 添加社交平台(例如 GitHub)登录支持 #\n\n在 Auth0,第三方平台登录是通过配置 Social Connections 完成的。\n\nAuth0 不支持禁止通过社交平台注册。如果希望只允许社交平台登录到已有的 IdP 账户,需要配置帐号链接,将通过社交平台登录的 IdP 账户链接到其它数据源的主 IdP 账户,并确保主 IdP 账户是社交平台数据源的用户无法完成获得 ID token 的流程。\n\n步骤和上面差不多,只是进行操作的站点反过来:\n\n 1. 在 GitHub 创建 application,填入 Application name、Homepage URL 和 Authorization callback URL,获得 Client ID 和 Client Secret\n * GitHub 的 OAuth app 可以挂靠在用户帐号下方(在这里创建),也可以挂靠在组织下方(在 `https://github.com/organizations/[org-name]/settings/applications` 创建)\n * 填入值的示例:\n * Application name: My Auth0\n * Homepage URL: `https://example.jp.auth0.com`\n * Authorization callback URL: `https://example.jp.auth0.com/login/callback`\n * 获得值的示例:\n * Client ID: `Ov23livDwfjz72TrrCgo`\n * 点击 \"Generate a new client secret\",得到 Client Secret: `95ea5ac40e17d3aba502fadbd37c15bf4b212d42`\n 2. 在 Auth0 的 Authentication > Social 页面下创建 GitHub 社交平台登录方式,填入上述值,保存\n 3. 搞定!现在就可以点击 \"Try Connection\" 尝试登录了。\n\n\n\n* * *\n\n 1. `email` claim 中的邮件地址不一定是经过 IdP 服务验证的。如果 `email` 未被验证就被应用信任,可能会导致应用内的账户被第三方 takeover。 ↩︎\n\n\n",
"title": "用 Auth0 玩儿基于 OpenID Connect 的 SSO",
"updatedAt": "2025-01-28T00:00:00.000Z"
}