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

Blazor Open ID Connect认证报「请求包含多个客户端凭证」错误如何排查

问题描述

我通过如下方案在Blazor中实现Open ID Connect认证逻辑:

Startup.cs 代码

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        this.Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddServerSideBlazor();
        services.AddSignalR(e =>
        {
            e.MaximumReceiveMessageSize = 102400000;
        });
        services.AddBlazoredModal();
        services.AddHttpClient();
        services.AddScoped<AccessTokenStorage>();
        services.AddAuthentication(opt =>
        {
            opt.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            opt.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            opt.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        }).AddCookie().AddOpenIdConnect("oidc", options =>
        {
            options.Authority = Credentials.Authority;
            options.ClientId = Credentials.ClientId;
            options.ClientSecret = Credentials.ClientSecret;
            options.ResponseType = "code";
            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;
            options.UseTokenLifetime = false;
            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name" };

            options.Events = new OpenIdConnectEvents
            {
                OnAccessDenied = context =>
                {
                    context.HandleResponse();
                    context.Response.Redirect("/");
                    return Task.CompletedTask;
                },
            };
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }
}

Login.cshtml.cs 核心代码

public class LoginModel : PageModel
{
    public async Task OnGet(string redirectUri)
    {
        await HttpContext.ChallengeAsync("oidc", new AuthenticationProperties { 
        RedirectUri = redirectUri });
    }
}

报错现象

上述代码对接demo.identityserver.io时运行完全正常,但切换为公司内部身份提供商后偶发如下报错:

FBTOAU228E The request included multiple client credentials. OAuth 2.0 protocol requests can have one client credential only. For example, the request cannot have client credentials in both the BA header and the request body.

该报错看似随机触发,但在浏览器中删除aspnetcore cookie后必然复现,正常情况下删除cookie后应跳转至登录页,实际却抛出上述错误,且对接demo.identityserver.io时从未出现。


问题结论

该问题不属于Blazor框架本身故障,也不属于身份提供商的故障,是ASP.NET Core默认OpenIdConnect处理器的配置与内部身份提供商的严格校验规则不匹配导致的。

根因说明

报错码FBTOAU228E是IBM Security Verify Access身份提供商的专有错误码,这类身份提供商对OAuth 2.0协议校验规则更严格,不允许令牌请求同时在Basic Auth请求头和请求体中携带client_id、client_secret凭证。
ASP.NET Core默认的OpenIdConnect处理器配置ClientSecret后,会默认同时在两个位置传递客户端凭证,demo.identityserver.io做了兼容处理所以不会报错,内部IdP严格遵循协议要求就会直接拒绝请求。删除Cookie后必然复现,是因为Cookie清空后会触发完整的授权码流程,需要向IdP发起令牌请求,此时就会触发凭证重复的校验规则。

修复方案

在Startup.cs的AddOpenIdConnect配置块中新增一行配置,指定客户端凭证仅通过请求体传递即可:

options.ClientCredentialMethod = OpenIdConnectClientCredentialMethod.FormPost;

修改后的配置片段参考:

.AddOpenIdConnect("oidc", options =>
{
    options.Authority = Credentials.Authority;
    options.ClientId = Credentials.ClientId;
    options.ClientSecret = Credentials.ClientSecret;
    // 新增这行指定凭证传递方式
    options.ClientCredentialMethod = OpenIdConnectClientCredentialMethod.FormPost;
    options.ResponseType = "code";
    // 其余原有配置保持不变
});

如果修改后仍报错,检查是否有全局HttpClient拦截器主动添加了Basic Auth头,避免重复传递凭证即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 22:45:08