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

ASP.NET Core 7.0 Web API对接Google的OIDC流程问题

解决Google OIDC回调中code参数为null的问题及Web API认证实现方案

问题根源分析

你当前的代码存在两个核心问题:

  1. 回调方法的参数绑定错误:Google的OIDC回调会将code放在URL查询参数中,但你用object code = null的方式无法正确绑定到该参数。
  2. 默认认证配置不匹配纯API场景:你将Google认证设为默认认证方案,但纯API服务的核心认证应该是JWT Bearer,且默认的Google认证中间件已经自动处理了code到令牌的交换流程,无需手动提取code。

一、修正认证服务配置

调整认证方案优先级,添加JWT Bearer支持(供其他微服务验证令牌),并配置Google认证保存令牌信息:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

// 注册服务
services.AddAuthentication(options =>
{
    // API默认用JWT Bearer做认证
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    // 发起认证挑战时用Google方案
    options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
// 添加JWT Bearer认证(供其他微服务验证令牌)
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = "你的身份微服务域名", // 示例:https://identity.yourdomain.com
        ValidAudience = "你的API资源标识", // 示例:api://your-service-api
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("至少32位长度的安全签名密钥"))
    };
})
.AddGoogle(options =>
{
    options.ClientId = "你的Google Client ID";
    options.ClientSecret = "你的Google Client Secret";
    // 回调路径必须与Google开发者控制台配置的完全一致
    options.CallbackPath = "/api/v1/Identity/callback";
    // 开启令牌保存,用于后续获取Google的ID Token和Access Token
    options.SaveTokens = true;
    // 声明需要获取的用户信息范围
    options.Scope.Add("openid");
    options.Scope.Add("email");
    options.Scope.Add("profile");
});

services.AddAuthorization();
// 注册自定义令牌生成服务
services.AddScoped<ITokenService, TokenService>();

二、修正控制器代码

利用认证中间件的自动处理,从HttpContext中直接获取认证结果和令牌,无需手动处理code:

using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Route("api/v1/[controller]/[action]")]
[ApiController]
public class IdentityController : ControllerBase
{
    private readonly ITokenService _tokenService;

    public IdentityController(ITokenService tokenService)
    {
        _tokenService = tokenService;
    }

    [AllowAnonymous]
    [HttpGet]
    public IActionResult SignIn()
    {
        var authProps = new AuthenticationProperties
        {
            IsPersistent = false,
            RedirectUri = Url.Action("Callback", "Identity")
        };
        // 发起Google认证挑战
        return Challenge(GoogleDefaults.AuthenticationScheme, authProps);
    }

    [AllowAnonymous]
    [HttpGet]
    public async Task<IActionResult> Callback()
    {
        // 获取Google认证结果
        var authResult = await HttpContext.AuthenticateAsync(GoogleDefaults.AuthenticationScheme);
        if (!authResult.Succeeded)
        {
            return BadRequest("Google认证失败");
        }

        // 从认证结果中提取Google颁发的令牌
        var googleIdToken = authResult.Properties.GetTokenValue("id_token");
        var googleAccessToken = authResult.Properties.GetTokenValue("access_token");

        // 提取用户基本信息
        var userId = authResult.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
        var email = authResult.Principal.FindFirstValue(ClaimTypes.Email);
        var userName = authResult.Principal.FindFirstValue(ClaimTypes.Name);

        // 生成供自身微服务使用的JWT令牌
        var apiJwt = _tokenService.GenerateJwtToken(userId, email, userName);

        // 返回令牌信息给客户端
        return Ok(new
        {
            AccessToken = apiJwt,
            GoogleIdToken = googleIdToken,
            ExpiresIn = 3600 // 令牌过期时间(秒)
        });
    }

    [Authorize]
    [HttpGet]
    public async Task<IActionResult> Logout()
    {
        // 清除Google认证会话和本地认证状态
        await HttpContext.SignOutAsync(GoogleDefaults.AuthenticationScheme);
        await HttpContext.SignOutAsync(JwtBearerDefaults.AuthenticationScheme);
        return Ok("登出成功");
    }
}

三、实现自定义JWT令牌生成服务

用于生成供内部微服务使用的JWT令牌:

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

public interface ITokenService
{
    string GenerateJwtToken(string userId, string email, string userName);
}

public class TokenService : ITokenService
{
    private readonly IConfiguration _config;

    public TokenService(IConfiguration config)
    {
        _config = config;
    }

    public string GenerateJwtToken(string userId, string email, string userName)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, userId),
            new Claim(ClaimTypes.Email, email),
            new Claim(ClaimTypes.Name, userName)
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: DateTime.UtcNow.AddHours(1),
            signingCredentials: creds);

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

四、关键注意事项

  1. Google控制台配置:确保/api/v1/Identity/callback已添加到Google Cloud Console中OAuth 2.0客户端的「授权重定向URI」列表,否则会回调失败。
  2. 中间件顺序:必须保证app.UseAuthentication()在app.UseAuthorization()之前执行,否则认证逻辑不会生效。
  3. 密钥安全:JWT签名密钥必须使用环境变量存储,禁止硬编码到代码中。

内容的提问来源于stack exchange,提问作者Dr. Strangelove

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 00:15:35