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

如何为.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 00:03:14