一套接口,接入 31 个第三方登录
CollectiveOAuth 是 .NET 平台的 OAuth 登录聚合库。生成授权地址、处理回调、换取用户信息,在每个平台上都是同样的两个调用。
authorize()login()- 31 个平台
- .NET Framework 4.6.2+、.NET Standard 2.0、.NET 8
- 行为与 Java 版 JustAuth 逐项比对测试
安装
目前通过源码引用使用。克隆仓库后,在你的项目里引用类库项目:
git clone https://github.com/geekoutnet/CollectiveOAuth.git
dotnet add 你的项目.csproj reference CollectiveOAuth/Come.CollectiveOAuth/Come.CollectiveOAuth.csproj类库同时编译三个目标框架,引用时会自动选择匹配的版本:
| 你的项目 | 使用的版本 |
|---|---|
| ASP.NET Core / .NET 8 及以上 | net8.0 |
| ASP.NET MVC 5 / .NET Framework 4.6.2 及以上 | net462 |
| 其他 .NET Standard 2.0 兼容项目 | netstandard2.0 |
快速开始
以 GitHub 登录为例。其他平台只是换一个请求类,用法完全相同。
-
在第三方平台创建应用
拿到
clientId、clientSecret,并把回调地址登记为你站点上的一个地址,例如https://your.site/oauth2/callback/github。 -
注册 state 缓存
state 用来防止 CSRF:生成授权地址时写入缓存,回调时校验并删除。ASP.NET Core 中注册一次即可:
C#.NET Framework 项目不需要这一步,默认使用进程内缓存。
// Program.cs builder.Services.AddDistributedMemoryCache(); builder.Services.AddSingleton<IAuthStateCache, DistributedAuthStateCache>(); -
跳转到授权页
C#
var config = new ClientConfig { clientId = "你的 Client ID", clientSecret = "你的 Client Secret", redirectUri = "https://your.site/oauth2/callback/github" }; var request = new GithubAuthRequest(config, authStateCache); return Redirect(request.authorize(AuthStateUtils.createState())); -
在回调里换取用户信息
第三方平台会带着
code和state回到你的回调地址。把参数绑定到AuthCallback,交给同一个请求类:C#public IActionResult Callback(AuthCallback callback) { var request = new GithubAuthRequest(config, authStateCache); AuthResponse response = request.login(callback); if (!response.ok()) { return BadRequest(response.msg); // 例如 "Illegal state [GITHUB]" } var user = (AuthUser)response.data; // user.uuid 是该平台下的唯一标识,user.token 里是 access_token 等令牌信息 return Ok(user.nickname); }
Come.AspNetCore.Sample(.NET 8)和 Come.Web.Sample(MVC 5)是完整示例:按平台名创建请求类、从配置文件读取参数,访问 /OAuth2/Authorization?authSource=GITHUB 即可走通。配置项
所有平台共用 ClientConfig。大多数平台只需要前三项,个别平台需要额外字段,见支持的平台。
| 字段 | 说明 |
|---|---|
clientId | 平台分配的应用 ID(AppKey / AppID)。必填。 |
clientSecret | 应用密钥。必填。支付宝填应用私钥。 |
redirectUri | 回调地址,必须以 http:// 或 https:// 开头,并与平台后台登记的一致。 |
scopes | 自定义授权范围列表。不填时使用该平台的默认范围。 |
httpConfig | 超时(毫秒)与代理,见超时与代理。 |
ignoreCheckState | 为 true 时回调不校验 state。会失去 CSRF 防护,仅在确有需要时使用。 |
ignoreCheckRedirectUri | 为 true 时不校验回调地址格式。 |
pkce | 开启 PKCE 模式(支持该模式的平台)。 |
alipayPublicKey | 支付宝公钥。支付宝必填。 |
agentId | 企业微信应用 AgentId。企业微信扫码必填。 |
stackOverflowKey | Stack Overflow 的 Key。该平台必填。 |
domainPrefix | 团队域名前缀。Coding 必填。 |
tenantId | 微软 Entra ID 租户,默认 common。 |
unionId | 设为 "true" 时,QQ 登录同时获取 unionid(需先在 QQ 互联申请权限)。 |
从配置文件读取
示例项目按 CollectiveOAuth_{平台名}_{字段} 的规则,从 AppSettings 节点读取配置:
{
"AppSettings": {
"CollectiveOAuth_GITHUB_ClientId": "...",
"CollectiveOAuth_GITHUB_ClientSecret": "...",
"CollectiveOAuth_GITHUB_RedirectUri": "https://your.site/oauth2/callback?authSource=GITHUB"
}
}ASP.NET Core 中调用一次 AppSettingUtils.UseConfiguration(configuration) 后,环境变量和 appsettings.{环境}.json 也会生效。
支持的平台
选择一个平台,查看要用的请求类、额外配置和回调参数。
state 与缓存
authorize() 会把 state 写入缓存,login() 校验通过后立即删除。所以同一个回调地址只能使用一次,被截获的回调无法重放。
| 实现 | 适用场景 |
|---|---|
DefaultAuthStateCache | 默认实现,进程内缓存,3 分钟过期。适合单机部署。 |
DistributedAuthStateCache | 基于 IDistributedCache。多台服务器部署时,回调可能落到另一台机器,必须使用它并接入 Redis 等共享缓存。 |
// 多实例部署:换成 Redis(需要 Microsoft.Extensions.Caching.StackExchangeRedis)
builder.Services.AddStackExchangeRedisCache(o => o.Configuration = "localhost:6379");
builder.Services.AddSingleton<IAuthStateCache, DistributedAuthStateCache>();也可以实现 IAuthStateCache 接口,接入你自己的存储。
超时与代理
默认超时为 3 秒,与 JustAuth 一致。访问海外平台较慢,或者需要走代理时,设置 httpConfig:
config.httpConfig = new HttpConfig
{
timeout = 15000, // 毫秒,小于等于 0 表示不限制
proxy = new WebProxy("http://127.0.0.1:7890")
};第三方平台返回非 2xx 状态码时,login() 返回失败,msg 中带有状态码和响应内容的开头部分,便于排查。
返回结果与错误码
login()、refresh()、revoke() 都返回 AuthResponse:code 为 2000 表示成功,此时 data 是结果对象;否则 msg 说明原因。配置不全等问题会在创建请求类时直接抛出 AuthException,其中的 errorCode 与下表一致。
| code | 含义 | 常见原因 |
|---|---|---|
| 2000 | 成功 | |
| 5000 | 失败 | 第三方平台返回了错误,看 msg |
| 5001 | 未实现 | 该平台没有刷新或撤销功能 |
| 5002 | 参数不完整 | 缺少 clientId、clientSecret 或平台必填的额外字段 |
| 5003 | 不支持的操作 | 平台没有提供对应的接口 |
| 5006 | 回调地址不合法 | 没有填写,或者不以 http/https 开头;支付宝不能使用 localhost |
| 5008 | code 不合法 | 回调里缺少授权码 |
| 5009 | state 不合法 | state 已使用、已过期(默认 3 分钟),或回调到了另一台没有共享缓存的服务器 |
| 5010 | 缺少 refresh token | |
| 5011–5016 | 令牌、kid、team id、client id/secret、企业微信 agentId 不合法 |
刷新与撤销授权
支持的平台见平台详情中的“刷新令牌”“撤销授权”两项。不支持时,调用会抛出 AuthException(5001 或 5003)。
AuthResponse refreshed = request.refresh(user.token);
if (refreshed.ok())
{
var token = (AuthToken)refreshed.data;
}与 JustAuth 的差异
授权地址、发出的请求、解析出的用户信息,都与 Java 版 JustAuth 1.16.7 逐项比对测试过。以下几处是有意保留的不同:
- state 校验通过后立即删除,防止回调被重放。JustAuth 不删除。
- 授权地址中的参数默认做 URL 编码,回调地址带查询参数也不会出错。
- 第三方返回非 2xx 时给出状态码和响应内容,不再吞掉错误。
- 提供
DistributedAuthStateCache,支持多实例部署。 - API 保持 C# 版原有命名:配置类叫
ClientConfig,部分平台名也不同,例如小米是XIAOMI(JustAuth 为MI)。
常见问题
回调总是返回 “Illegal state”
state 只能用一次,默认 3 分钟过期。刷新回调页面、授权页停留过久,或多台服务器没有共享缓存,都会出现这个错误。多实例部署请使用 DistributedAuthStateCache。
升级后访问海外平台超时
默认超时从旧版的 100 秒改成了 3 秒,与 JustAuth 一致。请通过 httpConfig.timeout 调大,或者配置代理。
创建请求类时抛出 AuthException
配置会在创建时校验。5002 表示缺少必填项,5006 表示回调地址不合法,对照配置项检查即可。
用户的性别是空的
平台没有返回性别时,gender 为 null,不会再默认显示为“女”。