Steam OAuth 和 Telegram OAuth 与 Casdoor:通过 OIDC 添加非标准社交登录
不存在“Steam OAuth”:Steam 使用 OpenID 2.0 对用户进行身份验证,并为您提供一个 64 位的 SteamID,仅此而已。Telegram 的经典登录组件也不是 OAuth:它会向您的页面提供一组用户字段以及一个使用您的机器人令牌进行验证的 HMAC-SHA256 哈希值。现在 Telegram 还在 oauth.telegram.org 运行一个真正的 OpenID Connect 提供商。如果您在应用和这些提供商之间放置 Casdoor,Casdoor 将执行协议特定的检查,而您的应用只与 Casdoor 通信 OpenID Connect,就像它与 Google 或 GitHub 通信一样。
提供商概览
| Provider | Protocol | What identifies the user | Email available? | Casdoor provider type |
|---|---|---|---|---|
| Steam | OpenID 2.0 at https://steamcommunity.com/openid/login, plus the Steam Web API for the profile | SteamID64, taken from the claimed ID https://steamcommunity.com/openid/id/<steamid> | No | Steam |
| Telegram (Login Widget) | Signed widget data, HMAC-SHA256 keyed with the SHA-256 of the bot token | Telegram user id | No | Telegram |
| Telegram (OpenID Connect) | OIDC authorization code flow with PKCE at https://oauth.telegram.org | sub claim of the ID token | No email claim; phone with the phone scope | Custom |
| X (Twitter) | OAuth 2.0 authorization code with PKCE | X user ID from /2/users/me | Not with Casdoor's default scopes | Twitter |
| OAuth 2.0 authorization code | App-scoped user ID | Only if the profile has a valid primary email | Facebook |
本文其余部分将详细介绍每种协议实际返回的内容以及 Casdoor 如何处理这些内容。
Steam:OpenID 2.0,无 OAuth
Steam 开发者页面将 Steam 描述为 OpenID 提供商:您的网站将用户发送到 Steam,Steam 返回形式为 https://steamcommunity.com/openid/id/<steamid> 的声明 ID。末尾的数字是用户的 64 位 SteamID。没有授权码,没有访问令牌,没有范围,也没有电子邮件:OpenID 响应或 Web API 配置文件都不包含电子邮件。
这就是为什么搜索“steam oauth”找不到有用的结果:该协议是 OpenID 2.0,即 OpenID Connect 的前身,因此 OAuth 2.0 或 OIDC 客户端库无法直接与 Steam 通信。
Casdoor 为 Steam 登录执行的操作:
- Casdoor 登录页面构建一个指向
https://steamcommunity.com/openid/login的 OpenID 2.0checkid_setup请求,其中openid.realm设置为 Casdoor 源,openid.return_to设置为 Casdoor 的/callback页面。 - 当 Steam 重定向回来时,Casdoor 使用
openid.mode=check_authentication将签名字段发布回 Steam。只有当 Steam 回答is_valid:true时,它才会继续。它还会检查openid.claimed_id是否匹配steamcommunity.com/openid/id/<digits>模式,并从中提取 SteamID。 - 使用您输入为提供商 Client secret 的 Steam Web API 密钥,Casdoor 调用
ISteamUser/GetPlayerSummaries以获取个性化名称和头像。
生成的 Casdoor 用户将以 SteamID64 作为用户名,以 Steam 昵称作为显示名称,并包含 Steam 头像。电子邮件字段保持为空。Casdoor 会故意丢弃底层库放置的占位符文本。如果你的应用需要电子邮件地址,请在注册后收集它。
设置只需两分钟:在 Steam 的密钥页面为你的 Casdoor 域名注册一个 Web API 密钥,创建一个类型为 Steam 的提供商,并将该密钥粘贴为客户端密钥。Steam 提供商指南中有详细步骤。
Telegram:登录小部件及其 HMAC 校验
经典的 Telegram 登录是一个 JavaScript 小部件。你通过 @BotFather 创建一个机器人,使用 /setdomain 将你的域名链接到该机器人,并嵌入小部件。当用户在 Telegram 中确认时,小部件要么重定向到 data-auth-url 中的 URL,要么调用一个 JavaScript 函数,传递 id、first_name、last_name、username、photo_url、auth_date 和 hash。
该负载中没有任何内容是令牌。任何人都可以将这些字段输入到 URL 中,因此服务器必须检查 hash。Telegram 文档中描述的校验方法如下:
data_check_string = all received fields except hash,
as key=value, sorted by key, joined with "\n"
secret_key = SHA256(bot_token)
valid = hex(HMAC_SHA256(data_check_string, secret_key)) == hash
Telegram 还建议你检查 auth_date,以防止旧数据被重放,但它让你自行决定有效的时间窗口。
在 Casdoor 中,Telegram 提供商类型接受机器人用户名(不带 @)作为 Client ID,并接受机器人令牌作为 Client secret。当用户点击 Telegram 按钮时,Casdoor 会打开其自身的 /telegram-login 页面,该页面从 telegram.org 加载 telegram-widget.js,并使用你的机器人用户名,同时将 data-auth-url 指向 Casdoor 的 /callback。回调收集这些字段,然后 Casdoor 后端执行以下操作:
- 从除
hash之外的所有字段重建数据校验字符串,对其进行排序,并使用SHA256(bot token)作为密钥对 HMAC-SHA256 与hash进行比较; - 如果
auth_date超过 24 小时,则拒绝该数据; - 使用数字 Telegram
id作为身份标识,使用username作为用户名,使用名字和姓氏作为显示名称,并使用photo_url作为头像。
Telegram 用户名是可选的。当用户没有用户名时,Casdoor 会生成 telegram_<id>,该值在每次登录时保持不变。小部件从不发送电子邮件地址或电话号码,因此仅通过 Telegram 注册的 Casdoor 用户既没有电子邮件也没有电话号码。有关 BotFather 的步骤,请参阅 Telegram 提供商指南。
Telegram 现在也提供 OpenID Connect
Telegram 的“Log In With Telegram”页面现在记录了标准的 OpenID Connect 流程,旧的 iframe 小部件文档已移至归档页面。根据 Telegram 文档的事实如下:
- 发现文档:
https://oauth.telegram.org/.well-known/openid-configuration。授权端点https://oauth.telegram.org/auth,令牌端点https://oauth.telegram.org/token,密钥位于https://oauth.telegram.org/.well-known/jwks.json。 - 在 @BotFather 中,你打开机器人的 Login Widget 设置,注册允许的 URL(来源和确切的重定向 URI),并获取客户端 ID 和客户端密钥。
- 授权码流程,推荐使用 PKCE 的
S256方法。令牌请求使用 HTTP Basic 认证,包含客户端 ID 和密钥。 - 范围:
openid是必需的;profile添加姓名、preferred_username和picture;phone在用户同意下添加phone_number。没有电子邮件声明。 - 没有 UserInfo 端点。所有用户数据都在 ID 令牌中,其
iss为https://oauth.telegram.org,其aud为你的机器人 ID。
你可以将此流程与 Casdoor 的自定义 OAuth 提供商连接起来。将 Auth URL 和 Token URL 设置为上述端点,将 Scope 设置为 openid profile(如果需要,添加 phone),开启 Enable PKCE,并将 UserInfo URL 留空。在 BotFather 中将 Casdoor 的 https://<your-casdoor-host>/callback 注册为允许的 URL。由于没有 UserInfo URL,Casdoor 将从令牌响应中的 ID 令牌获取用户信息:它要求存在 sub,拒绝过期的令牌,并检查 aud 是否包含客户端 ID。如果没有用户映射,sub 将成为用户 ID,preferred_username 成为用户名,name 成为显示名称,picture 成为头像,而 phone_number 将填充电话号码。Casdoor 登录页面会识别其 auth URL 指向 oauth.telegram.org 的自定义提供商,并为其保持简短的 OAuth state。
请注意,通过小部件登录的 Telegram 用户和通过 OIDC 登录的同一人不会自动成为同一个 Casdoor 用户。小部件的身份标识是数字 id,而 OIDC 的身份标识是 sub,Telegram 自己的示例显示这两个值不同。请为每个应用选择一种方法。
X (Twitter) 和 Facebook
这两者都是普通的 OAuth 2.0,但每个都有值得了解的细节。
X 在其 OAuth 2.0 授权 URL 上需要 PKCE 挑战。Casdoor 的 Twitter 类型会为每次登录生成新的代码验证器,发送 S256 挑战,并在令牌交换中传递验证器,因此除了客户端 ID 和密钥外无需其他配置。Casdoor 请求 users.read tweet.read 范围,并从 /2/users/me 读取用户信息,这提供了用户 ID、句柄和显示名称。它不请求电子邮件字段,因此请预期 X 用户可能没有电子邮件地址。Twitter 提供商指南涵盖了应用设置。
Facebook 返回应用范围内的用户 ID:同一个人在不同的 Facebook 应用中具有不同的 ID,因此迁移到新的 Facebook 应用意味着除非你通过电子邮件链接它们,否则你的用户将无法匹配其旧记录。Casdoor 请求 email,public_profile,并从 Graph API 读取 id、name、picture 和 email。当配置文件没有有效地址时,Facebook 会省略 email。参见 Facebook 提供商指南。
你的应用如何看待这一切:纯 OIDC
你的应用从不直接与 Steam、Telegram、X 或 Facebook 通信。它是 Casdoor 的 OIDC 客户端,只需配置一次 Casdoor 的 issuer,即 https://<your-casdoor-host>/.well-known/openid-configuration。你在 Casdoor 应用的 Providers 选项卡中添加社交提供商,它们的按钮就会出现在 Casdoor 登录页面上。
在任何此类登录后,Casdoor 会查找或创建 Casdoor 用户,并与你的应用完成标准的授权码流程。你的应用收到的 ID 令牌由 Casdoor 签名,其 sub 是 Casdoor 用户 ID,而不是 SteamID 或 Telegram ID。因此,你的应用不需要 OpenID 2.0 库、HMAC 代码、针对每个提供商的 PKCE 特殊处理,也不需要为没有邮箱的用户设置特殊情况。原始提供商 ID 存储在 Casdoor 用户上,例如在 steam 或 telegram 字段中,以备你将来需要时使用。
需要考虑以下几点:
- 账户关联。 Casdoor 通过已验证的电子邮件、已验证的电话或用户名将社交登录与现有用户关联,顺序取决于应用程序的绑定规则设置。Steam 和基于小部件的 Telegram 既不提供电子邮件也不提供电话,因此它们会创建一个单独的用户,除非用户从已登录的账户中关联该提供商。请参阅 Casdoor 如何关联账户。
- 额外的个人资料字段。 传递原始数据的提供商(如基于 Goth 的 Steam 类型和自定义提供商)可以通过用户映射填充更多用户字段。
- 客户端侧。 任何标准库都可以与 Casdoor 配合使用。连接标准 OIDC 客户端列出了端点,而 Spring Boot SSO with OIDC 文章展示了一个完整的应用程序。
参考资料
- Steam Web API 和 OpenID: steamcommunity.com/dev
- Steamworks, User Authentication and Ownership: partner.steamgames.com/doc/features/auth
- Telegram, Log In With Telegram (OpenID Connect): core.telegram.org/bots/telegram-login
- Telegram, Login Widget (legacy): core.telegram.org/widgets/login-legacy
- X, OAuth 2.0 Authorization Code Flow with PKCE: docs.x.com
- Meta, Graph API User reference: developers.facebook.com/docs/graph-api/reference/user
- Casdoor 源代码:
idp/telegram.go,idp/goth.go,idp/custom.go