Asp.Net5集成Google OAuth2时重定向不匹配问题求助
我在Asp.Net5中集成Google OAuth2时遇到了重定向不匹配问题。目前Google控制台中配置的回调地址为https://localhost:4200/signin-google,但我的应用并未设置该路由,因此触发Error 400: redirect_uri_mismatch错误。
若将Google控制台和GoogleOptions的CallbackPath均设置为https://localhost:4200/api/google/response,挑战重定向可成功完成,且能获取到包含谷歌用户信息(姓名、邮箱等)的Cookie,但/api/google/response接口始终未触发(控制台无输出)。
我发现许多教程仅在Google控制台配置https://localhost:4200/signin-google,既不设置CallbackPath也不配置对应路由却能正常运行,这让我十分困惑。请问:
- 我是否应回到两端都配置https://localhost:4200/api/google/response的方案,排查接口未触发的原因?
- 是否需要在应用中添加/signin-google路由?
- 为何多数教程无需配置CallbackPath和路由就能正常工作,而我的情况却不行?
相关代码
Startup.cs
public void ConfigureServices(IServiceCollection services) { // configurations services.Configure<EnvironmentVariables>(Configuration.GetSection(EnvironmentVariables.EnvironmentVariable)); services.Configure<RabbitMQSettingsOptions>(Configuration. GetSection(RabbitMQSettingsOptions.RabbitMQSettings)); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { this.SetUpCookieAuthentication(options); }); services.AddAuthentication().AddGoogle(options => { this.SetUpGoogleAuthentication(options); }); services.AddCors(options => { this.SetUpCorsPolicy(options); }); ... } private void SetUpGoogleAuthentication(GoogleOptions options) { ... // set GoogleOptions //options.CallbackPath = new PathString(path); options.ClientSecret = googleAuthentication.ClientSecret; options.ClientId = googleAuthentication.ClientId; } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } //app.UseHttpsRedirection(); app.UseRouting(); app.UseCors("CorsPolicy"); // app.UseSession(); app.UseAuthentication(); app.UseAuthorization(); app.UseCookiePolicy(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
GoogleController.cs
using AutoMapper.Internal; using Domain.DDtos.Users; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Google; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Newtonsoft.Json; using ServiceLayer.Dtos.Users; using ServiceLayer.Interfaces; using ServiceLayer.Services; using ServiceLayer.Types; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; namespace Eshop.Controllers { [Route("api/google")] [ApiController] public class GoogleController : ControllerBase { private IOptions<EnvironmentVariables> _envOptions; private readonly IEnvironmentVariableService _envService; private readonly IUserService _userService; public GoogleController(IOptions<EnvironmentVariables> envOptions, IEnvironmentVariableService envService, IUserService userService) { _envOptions = envOptions; _envService = envService; _userService = userService; } [HttpGet("signin")] public async Task<ActionResult> GetGoogleSignInAsync() { var googleAuthenticationFile = await _envService.GetEnvironmentVariableValueAsync(_envOptions.Value.GoogleAuthentication); if (googleAuthenticationFile == null) throw new Exception("Could not find the actual google authentication file."); var googleAuthentication = JsonConvert.DeserializeObject<OAuth2Credentials>(googleAuthenticationFile); var redirectUri = googleAuthentication.RedirectUris.FirstOrDefault(); var properties = new AuthenticationProperties { RedirectUri = Url.Action("GoogleResponseAsync") }; return Challenge(properties, GoogleDefaults.AuthenticationScheme); } [HttpGet("response")] public async Task<ActionResult> GoogleResponseAsync() { Console.WriteLine("in response"); var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); if (!result.Succeeded) return Unauthorized(); Debug.WriteLine(JsonConvert.SerializeObject(result)); var userLinkDto = new UserLinkDto(); var claims = result.Principal.Identities.FirstOrDefault().Claims.Select(claim => new { claim.Issuer, claim.OriginalIssuer, claim.Type, claim.Value, }); claims.ForAll(claim => { var claimType = Constants.GoogleClaimsDict.GetValueOrDefault(claim.Type); var dtoProp = claimType != null ? userLinkDto.GetType().GetProperty(claimType) : null; if (dtoProp != null) dtoProp.SetValue(userLinkDto, claim.Value); }); return Ok(); } }
1. 是否应回到https://localhost:4200/api/google/response方案排查接口未触发?
不建议优先选择这个方案,先解决默认方案(使用/signin-google)的核心问题更高效。如果要排查自定义回调的问题,核心原因是:你在Challenge时设置的RedirectUri会覆盖GoogleOptions.CallbackPath的作用——Google OAuth回调到CallbackPath后,ASP.NET认证中间件会自动完成凭证校验、生成Cookie等操作,之后直接跳转到你设置的RedirectUri指向的接口,所以实际触发的是GoogleResponseAsync而非CallbackPath对应的地址,你不需要为CallbackPath编写接口。
2. 是否需要添加/signin-google路由?
不需要手动添加。/signin-google是ASP.NET OAuth认证中间件内置的回调地址,当未指定GoogleOptions.CallbackPath时,中间件会自动注册该路由并处理Google的回调请求,完成凭证校验、Claims生成、Cookie创建等操作,之后再跳转到Challenge时设置的RedirectUri。
3. 为什么多数教程无需配置就能正常工作?
你的代码存在**重复调用AddAuthentication**的错误:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(...); services.AddAuthentication().AddGoogle(...);
第二次调用AddAuthentication()会重置之前的认证配置,导致默认的Cookie认证方案被覆盖,Google认证的配置也未与Cookie认证关联。正确写法是在同一个AddAuthentication链中添加Google认证:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { this.SetUpCookieAuthentication(options); }) .AddGoogle(options => { this.SetUpGoogleAuthentication(options); });
修正后,中间件会正确使用默认Cookie方案,内置的/signin-google回调路由也会正常注册,此时只需在Google控制台配置https://localhost:4200/signin-google即可正常工作,无需手动添加路由或修改CallbackPath。另外你在GetGoogleSignInAsync中设置的RedirectUri是正确的,认证完成后会自动跳转到GoogleResponseAsync接口获取用户信息。
内容的提问来源于stack exchange,提问作者Tonys Ansonī Misirgis

