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
相关产品推荐
相关产品推荐

