You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core MVC中如何发起并存储多个OAuth2外部身份验证质询

这个需求可以实现,不需要创建多份认证Cookie,基于你现有的配置做少量调整即可:

1 调整认证服务配置

首先给两套OAuth方案分别设置独立的回调路径,避免回调逻辑冲突,同时保持SaveTokens = true的配置:

.AddScheme1 (options =>
{                
    options.ClientId = Configuration["Scheme1:ClientId"];
    options.ClientSecret = Configuration["Scheme1:ClientSecret"];
    options.Scope.Add("scope1");
    options.CallbackPath = "/signin-scheme1"; // 新增独立回调路径
    options.SaveTokens = true;                                
})
.AddScheme2(options =>
{
    options.ClientId = Configuration["Scheme2:ClientId"];
    options.ClientSecret = Configuration["Scheme2:ClientSecret"];                
    options.Scope.Add("scope1");  
    options.CallbackPath = "/signin-scheme2"; // 新增独立回调路径              
    options.SaveTokens = true;
});

2 调整登录流程为分步触发质询

OAuth协议本身不支持同时触发多个身份质询,按你预期的先后登录逻辑调整Controller代码即可:

// 第一步:触发第一个方案的登录
[HttpPost("~/signin")]
public IActionResult SignInFirst()
{
    var props = new AuthenticationProperties 
    { 
        RedirectUri = Url.Action("SignInSecond") 
    };
    return Challenge(props, "Scheme1");         
}

// 第一个方案登录成功后,触发第二个方案的登录
public async Task<IActionResult> SignInSecond()
{
    // 暂存第一个方案的令牌和声明
    var scheme1Result = await HttpContext.AuthenticateAsync("Scheme1");
    if (!scheme1Result.Succeeded) return RedirectToAction("SignInFirst");
    
    var props = new AuthenticationProperties
    {
        RedirectUri = Url.Action("CompleteSignIn"),
        // 把第一个方案的令牌暂存到Properties里带到下一步
        Items = 
        {
            ["scheme1_token"] = scheme1Result.Properties.GetTokenValue("access_token"),
            ["scheme1_userid"] = scheme1Result.Principal.FindFirstValue(ClaimTypes.NameIdentifier)
        }
    };
    return Challenge(props, "Scheme2");
}

// 两个方案都登录完成后,合并信息写入Cookie
public async Task<IActionResult> CompleteSignIn()
{
    var scheme2Result = await HttpContext.AuthenticateAsync("Scheme2");
    if (!scheme2Result.Succeeded) return RedirectToAction("SignInFirst");
    
    // 取出第一步暂存的Scheme1信息
    var scheme1Token = scheme2Result.Properties.Items["scheme1_token"];
    var scheme1UserId = scheme2Result.Properties.Items["scheme1_userid"];
    var scheme2Token = scheme2Result.Properties.GetTokenValue("access_token");
    var scheme2UserId = scheme2Result.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
    
    // 合并声明,创建身份
    var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
    identity.AddClaim(new Claim("scheme1_userid", scheme1UserId));
    identity.AddClaim(new Claim("scheme2_userid", scheme2UserId));
    
    // 把两个令牌都存入认证属性
    var authProps = new AuthenticationProperties();
    authProps.StoreTokens(new List<AuthenticationToken>
    {
        new AuthenticationToken { Name = "scheme1_access_token", Value = scheme1Token },
        new AuthenticationToken { Name = "scheme2_access_token", Value = scheme2Token }
    });
    
    // 统一写入Cookie
    await HttpContext.SignInAsync(
        CookieAuthenticationDefaults.AuthenticationScheme, 
        new ClaimsPrincipal(identity), 
        authProps);
    
    return RedirectToAction("Index", "Home");
}

3 获取指定方案的令牌和用户信息

后续业务逻辑中可以直接按名称取对应信息:

// 取对应方案的令牌
var scheme1Token = await HttpContext.GetTokenAsync("scheme1_access_token");
var scheme2Token = await HttpContext.GetTokenAsync("scheme2_access_token");

// 取对应方案的用户ID
var scheme1UserId = User.FindFirstValue("scheme1_userid");
var scheme2UserId = User.FindFirstValue("scheme2_userid");

关于你提到的SignInManager,它是ASP.NET Core Identity体系的组件,如果你没有用到Identity的用户存储逻辑,不需要引入它,上述自定义实现即可满足需求。

内容的提问来源于stack exchange,提问作者PeterX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 07:54:02