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

Asp.Net Web API集成Google OAuth时state缺失/无效问题求助

解决Asp.Net Web API集成Google OAuth时的"The oauth state was missing or invalid."错误

问题描述

我正在为搭配React前端的Asp.Net Web API集成Google OAuth认证,却持续遇到“The oauth state was missing or invalid.”错误。已查阅大量相关内容并尝试各类修复方案,但均未解决,同时对控制器的结构设计存在疑惑。当前已配置相关中间件(代码如下),尝试过添加/移除callbackPath、通过前端传递ID Token验证(但始终存在跨域问题),期望实现Google登录并保持指定时长的认证状态。

原中间件配置代码

using Hangfire;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using RestaurantManagement.API.Data;
using RestaurantManagement.API.Interfaces;
using RestaurantManagement.API.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Identity;
using RestaurantManagement.API.Models;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        // Configure System.Text.Json options
        options.JsonSerializerOptions.PropertyNamingPolicy = null; // Preserve capitalization
    });


// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddLogging();

builder.Services.AddScoped<IWeekService,WeekService>();
builder.Services.AddScoped<IShiftService, ShiftService>();
builder.Services.AddScoped<IEmployeeService, EmployeeService>();
builder.Services.AddScoped<IJobService, JobService>();
builder.Services.AddScoped<IConfigService, ConfigService>();

builder.Services.AddDbContext<DataContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("RestaurantManagementAPIContext"))
);

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowReactFrontend", builder =>
    {
        builder.WithOrigins("http://localhost:5173", "https://localhost:5173", "https://localhost:7075") //
               .AllowAnyHeader()
               .AllowAnyMethod()
               .AllowCredentials(); 
    });
});

builder.Services.AddIdentity<Employee, IdentityRole>(options => {
    
})
.AddEntityFrameworkStores<DataContext>()
.AddDefaultTokenProviders();

builder.Services.AddDistributedMemoryCache();

// Add session
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

builder.Services.AddAuthorization();

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
 .AddCookie(options =>
 {
     options.Cookie.Name = "ResturantManagmentApi.Cookie"; 
     options.ExpireTimeSpan = TimeSpan.FromDays(1); 
     options.Cookie.SameSite = SameSiteMode.None;
     options.Cookie.SecurePolicy = CookieSecurePolicy.Always ;

 })
.AddGoogle(options =>
{
    options.ClientId = "ClintId";
    options.ClientSecret = "ClientSecret";

});



builder.Services.AddHangfire((sp, config) =>
{
    var connectionString = sp.GetRequiredService<IConfiguration>().GetConnectionString("RestaurantManagementAPIContext");
    config.UseSqlServerStorage(connectionString);
});
builder.Services.AddHangfireServer();

builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Your API", Version = "v1" });

    // Add support for authentication
    c.AddSecurityDefinition("cookie", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.ApiKey,
        In = ParameterLocation.Cookie,
        Name = ".AspNetCore.Cookies" // This should match your cookie name
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "cookie" }
            },
            new string[] { }
        }
    });
});


var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();
app.UseRouting();
app.UseCors("AllowReactFrontend");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.UseHangfireDashboard("/hangfire");

//run recurring services
using (var scope = app.Services.CreateScope())
{
    var jobService = scope.ServiceProvider.GetRequiredService<IJobService>();
    jobService.RunWeekClosingRecurringJob();
    jobService.RunWeekFinalizingRecurringJob();
}

app.Run();

一、解决"The oauth state was missing or invalid."错误

这个错误核心原因是State参数的存储/传递异常、Cookie配置不兼容跨域或中间件顺序错误,按以下步骤修复:

1. 固定CallbackPath并同步Google控制台配置

显式设置Google OAuth的回调路径,避免自动生成路径不一致:

.AddGoogle(options =>
{
    options.ClientId = "你的实际ClientId";
    options.ClientSecret = "你的实际ClientSecret";
    options.CallbackPath = "/signin-google"; // 固定回调路径
});

同时在Google Cloud控制台的OAuth 2.0客户端ID设置中,将http://localhost:7075/signin-google和https://localhost:7075/signin-google添加到已授权的重定向URI列表。

2. 调整Cookie配置适配开发/生产环境

当前Cookie设置在开发环境过于严格,修改为环境适配模式:

.AddCookie(options =>
{
    options.Cookie.Name = "ResturantManagmentApi.Cookie";
    options.ExpireTimeSpan = TimeSpan.FromDays(1);
    // 开发环境用Lax,生产环境用None
    options.Cookie.SameSite = builder.Environment.IsDevelopment() ? SameSiteMode.Lax : SameSiteMode.None;
    // 开发环境关闭HTTPS强制要求
    options.Cookie.SecurePolicy = builder.Environment.IsDevelopment() ? CookieSecurePolicy.None : CookieSecurePolicy.Always;
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
})

3. 确认中间件顺序正确性

当前中间件顺序已符合要求,但需牢记:

  • UseSession()必须在UseAuthentication()之前
  • UseCors()必须在UseAuthentication()和UseAuthorization()之前

4. 开发环境临时禁用HTTPS重定向

如果本地用HTTP测试,注释掉app.UseHttpsRedirection();,避免重定向过程中丢失State参数。

二、解决跨域传递ID Token问题

若选择前端获取ID Token后传递给API验证,需做以下调整:

1. 配置Google控制台的JavaScript来源

在Google Cloud控制台的OAuth 2.0客户端ID设置中,添加http://localhost:5173和https://localhost:5173到已授权的JavaScript来源。

2. API端添加ID Token验证接口

创建AuthController处理登录逻辑:

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly SignInManager<Employee> _signInManager;
    private readonly IConfiguration _configuration;

    public AuthController(SignInManager<Employee> signInManager, IConfiguration configuration)
    {
        _signInManager = signInManager;
        _configuration = configuration;
    }

    [HttpPost("google-login")]
    public async Task<IActionResult> GoogleLogin([FromBody] GoogleLoginRequest request)
    {
        var settings = new GoogleJsonWebSignature.ValidationSettings
        {
            Audience = new List<string> { _configuration["Authentication:Google:ClientId"] }
        };

        var payload = await GoogleJsonWebSignature.ValidateAsync(request.IdToken, settings);

        // 查找或创建系统用户
        var user = await _signInManager.UserManager.FindByEmailAsync(payload.Email);
        if (user == null)
        {
            user = new Employee { UserName = payload.Email, Email = payload.Email };
            await _signInManager.UserManager.CreateAsync(user);
        }

        // 登录并生成认证Cookie
        await _signInManager.SignInAsync(user, isPersistent: false);
        return Ok(new { Message = "登录成功" });
    }
}

public class GoogleLoginRequest
{
    public string IdToken { get; set; }
}

3. 前端调用时携带Credentials

React端调用API时必须设置credentials: 'include',确保Cookie能跨域传递:

const response = await fetch('https://localhost:7075/api/auth/google-login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    credentials: 'include',
    body: JSON.stringify({ idToken: '从Google Auth获取的ID Token' })
});

三、控制器结构建议

  • 单独创建AuthController集中处理所有认证逻辑(登录、登出、身份验证)
  • 业务控制器通过[Authorize]特性保护需要权限的接口
  • 可封装基类控制器,统一处理用户信息获取等通用逻辑

四、调整后的关键配置示例

// 认证配置修正
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
    options.Cookie.Name = "ResturantManagmentApi.Cookie";
    options.ExpireTimeSpan = TimeSpan.FromDays(1);
    options.Cookie.SameSite = builder.Environment.IsDevelopment() ? SameSiteMode.Lax : SameSiteMode.None;
    options.Cookie.SecurePolicy = builder.Environment.IsDevelopment() ? CookieSecurePolicy.None : CookieSecurePolicy.Always;
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
    options.LoginPath = "/api/auth/login";
    options.AccessDeniedPath = "/api/auth/access-denied";
})
.AddGoogle(options =>
{
    options.ClientId = builder.Configuration["Authentication:Google:ClientId"];
    options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
    options.CallbackPath = "/signin-google";
});

// Swagger Cookie名称修正(匹配实际Cookie名称)
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "RestaurantManagement API", Version = "v1" });

    c.AddSecurityDefinition("cookie", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.ApiKey,
        In = ParameterLocation.Cookie,
        Name = "ResturantManagmentApi.Cookie"
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "cookie" }
            },
            new string[] { }
        }
    });
});

内容的提问来源于stack exchange,提问作者Avi_H

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:57:03