如何为.NET Core Web API从Google获取JWT?设计与问题排查
问题解答
一、设计合理性分析
你的设计思路是合理的:通过Google OAuth授权码流完成用户认证,后端拿到授权码后向Google换取JWT,再将JWT返回给MAUI客户端,客户端用这个JWT调用受保护的REST接口。这种方式符合OAuth2.0的最佳实践,授权码流比隐式流更安全,适合服务端场景。
二、如何获取Google授权码
默认情况下,ASP.NET Core的Google认证中间件会自动处理signin-google回调,完成授权码到access token的兑换,所以你在自定义的/api/auth/callback里拿不到授权码。要拿到授权码,需要修改Google认证的配置,开启保存令牌选项并通过事件拦截授权码:
1. 修改Google认证配置
在AddGoogle时添加SaveTokens = true,并通过OnAuthorizationCodeReceived事件捕获授权码,还可以手动用授权码换取Google签发的ID Token(JWT):
.AddGoogle(options => { IConfigurationSection googleAuthNSection = configuration.GetSection("Authentication:Google"); options.ClientId = googleAuthNSection["ClientId"]; options.ClientSecret = googleAuthNSection["ClientSecret"]; options.SaveTokens = true; // 保存所有令牌到AuthenticationProperties options.CallbackPath = "/signin-google"; options.Events = new OAuthEvents { OnAuthorizationCodeReceived = async context => { // 直接获取授权码 string authorizationCode = context.Code; // 手动调用Google令牌端点换取ID Token(JWT) var tokenClient = new HttpClient(); var tokenParams = new Dictionary<string, string> { ["code"] = authorizationCode, ["client_id"] = options.ClientId, ["client_secret"] = options.ClientSecret, ["redirect_uri"] = context.RedirectUri, ["grant_type"] = "authorization_code" }; var tokenResponse = await tokenClient.PostAsync( "https://oauth2.googleapis.com/token", new FormUrlEncodedContent(tokenParams) ); if (tokenResponse.IsSuccessStatusCode) { var tokenData = await tokenResponse.Content.ReadFromJsonAsync<Dictionary<string, string>>(); string idToken = tokenData["id_token"]; // Google签发的JWT // 将ID Token存入Properties,供后续回调控制器使用 context.Properties.SetTokenValue("id_token", idToken); } } }; })
2. 调整回调控制器逻辑
现在可以在自定义的Callback方法中,从AuthenticationProperties里获取授权码和ID Token,还可以生成后端自定义JWT返回给MAUI:
[HttpGet("callback")] [AllowAnonymous] public async Task<IActionResult> Callback() { var auth = await Request.HttpContext.AuthenticateAsync("Google"); if (!auth.Succeeded) { return BadRequest("认证失败"); } // 获取授权码 var authorizationCode = auth.Properties.GetTokenValue("code"); // 获取Google签发的ID Token var googleIdToken = auth.Properties.GetTokenValue("id_token"); // 可选:生成后端自定义JWT(也可以直接返回Google的ID Token) var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, auth.Principal.FindFirstValue(ClaimTypes.NameIdentifier)), new Claim(ClaimTypes.Email, auth.Principal.FindFirstValue(ClaimTypes.Email)) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var customJwt = new JwtSecurityToken( issuer: configuration["Jwt:Issuer"], audience: configuration["Jwt:Audience"], claims: claims, expires: DateTime.Now.AddHours(1), signingCredentials: creds ); var jwtString = new JwtSecurityTokenHandler().WriteToken(customJwt); // 返回JWT给MAUI客户端 return Ok(new { Token = jwtString }); }
3. 为什么自定义/signin-google控制器端点无效?
ASP.NET Core的Google认证中间件会在中间件层面优先处理CallbackPath对应的路由,不会走到控制器的路由逻辑。所以正确的方式是通过OnAuthorizationCodeReceived事件拦截授权码,而非自定义该路径的控制器。
三、额外说明
- 如果直接使用Google的ID Token作为REST服务的认证令牌,后端需要配置JWT验证逻辑,校验Token的签发者、受众、签名等信息。
- MAUI客户端拿到JWT后,每次调用REST接口时,需在请求头中添加
Authorization: Bearer {token}。
内容的提问来源于stack exchange,提问作者Brian Cooke
相关产品推荐
相关产品推荐

