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

ASP.NET Core 8中结合GoogleOpenId与Cookie认证,如何保留双认证身份?

问题背景

我有一个以Cookie认证为主要方式的ASP.NET Core 8应用,需要添加Google OpenID认证以访问Google Calendar。现有配置中,完成Google登录后,主Cookie认证的用户信息会被Google令牌覆盖,需要实现两种认证身份共存,不同端点对应不同认证方式。同时希望在.NET 8中实现旧版GoogleAuthorizationCodeFlow流程。

现有Program.cs配置:

builder.Services.AddAuthentication(options =>
    {
        options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
    {
        options.LoginPath = new PathString("/Account/Login");
    })
    .AddGoogleOpenIdConnect(GoogleOpenIdConnectDefaults.AuthenticationScheme, options =>
    {
        options.ClientId = clientId;
        options.ClientSecret = clientSecret;
    });

控制器代码示例:

[Authorize(AuthenticationSchemes = GoogleOpenIdConnectDefaults.AuthenticationScheme)]
public async Task<IActionResult> GoogleAuth([FromServices] IGoogleAuthProvider auth)
{
    // ...
}
[Authorize]
public Task<IActionResult> RegularAuth()
{
    var claims = HttpContext.User.Claims;
}
解决方案

一、实现两种认证身份共存

问题根源是Google OpenID Connect默认会将认证结果写入默认Cookie方案,覆盖原有用户身份。解决方法是为Google认证配置独立的Cookie存储方案:

1. 修改Program.cs认证配置

添加独立的Google Cookie方案,并指定Google OpenID Connect使用该方案存储身份:

builder.Services.AddAuthentication(options =>
{
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
// 主Cookie认证(用户名密码登录)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
    options.LoginPath = new PathString("/Account/Login");
})
// 专门用于Google认证的独立Cookie方案
.AddCookie("Google.Cookie")
.AddGoogleOpenIdConnect(GoogleOpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.ClientId = clientId;
    options.ClientSecret = clientSecret;
    // 指定Google认证后使用独立Cookie存储身份
    options.SignInScheme = "Google.Cookie";
    // 保存令牌,用于后续调用Google API
    options.SaveTokens = true;
    // 添加Google Calendar所需权限
    options.Scope.Add("https://www.googleapis.com/auth/calendar.readonly");
    // 回调路径需与Google开发者控制台配置一致
    options.CallbackPath = new PathString("/signin-google");
});

2. 调整控制器认证标记

修改需要Google认证的端点,指定使用独立的Google Cookie方案:

[Authorize(AuthenticationSchemes = "Google.Cookie")]
public async Task<IActionResult> GoogleAuth()
{
    // 获取Google访问令牌
    var accessToken = await HttpContext.GetTokenAsync("Google.Cookie", "access_token");
    // 使用令牌调用Google Calendar API
    // ...
}

此时:

  • 访问RegularAuth端点时,使用主Cookie认证,保留原有用户信息
  • 访问GoogleAuth端点时,使用独立的Google Cookie认证,不会覆盖主身份

二、实现旧版GoogleAuthorizationCodeFlow流程

通过Google.Apis.Auth.AspNetCore3包手动处理授权码流程,更灵活地控制令牌生命周期:

1. 安装依赖包

Install-Package Google.Apis.Auth.AspNetCore3
Install-Package Google.Apis.Calendar.v3

2. 注册Google授权服务

在Program.cs中添加:

builder.Services.AddGoogleAuth(options =>
{
    options.ClientId = clientId;
    options.ClientSecret = clientSecret;
});

3. 实现授权与API调用控制器

public class GoogleCalendarController : Controller
{
    private readonly IGoogleAuthProvider _authProvider;
    private readonly string _clientId = "你的Google客户端ID";
    private readonly string _clientSecret = "你的Google客户端密钥";

    public GoogleCalendarController(IGoogleAuthProvider authProvider)
    {
        _authProvider = authProvider;
    }

    // 发起Google授权请求
    public IActionResult Authorize()
    {
        var redirectUri = Url.Action(nameof(Callback), "GoogleCalendar", null, Request.Scheme);
        var authorizationUrl = _authProvider.GetAuthorizationUrl(redirectUri, new[] { CalendarService.Scope.CalendarReadonly });
        return Redirect(authorizationUrl);
    }

    // 授权回调,交换授权码获取令牌
    public async Task<IActionResult> Callback(string code, string state)
    {
        var redirectUri = Url.Action(nameof(Callback), "GoogleCalendar", null, Request.Scheme);
        var token = await _authProvider.ExchangeCodeForTokenAsync(code, redirectUri);

        // 将Google令牌存入主Cookie的Claims中(不覆盖原有用户身份)
        var currentIdentity = (ClaimsIdentity)User.Identity;
        currentIdentity.AddClaims(new[]
        {
            new Claim("google_access_token", token.AccessToken),
            new Claim("google_refresh_token", token.RefreshToken)
        });

        await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(currentIdentity));
        return RedirectToAction(nameof(Index));
    }

    // 使用令牌调用Google Calendar API
    public async Task<IActionResult> Index()
    {
        var accessToken = User.FindFirstValue("google_access_token");
        if (string.IsNullOrEmpty(accessToken))
        {
            return RedirectToAction(nameof(Authorize));
        }

        // 初始化GoogleAuthorizationCodeFlow
        var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = new ClientSecrets
            {
                ClientId = _clientId,
                ClientSecret = _clientSecret
            },
            Scopes = new[] { CalendarService.Scope.CalendarReadonly }
        });

        // 创建用户凭证
        var credential = new UserCredential(flow, User.Identity.Name, new TokenResponse
        {
            AccessToken = accessToken,
            RefreshToken = User.FindFirstValue("google_refresh_token")
        });

        // 调用Calendar API
        var service = new CalendarService(new BaseClientService.Initializer
        {
            HttpClientInitializer = credential
        });

        var events = await service.Events.List("primary").ExecuteAsync();
        return View(events.Items);
    }
}

这种方式将Google令牌附加到现有用户身份的Claims中,既保留原有Cookie认证信息,又能使用Google令牌调用API,完全兼容旧版GoogleAuthorizationCodeFlow逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 03:42:36