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

.NET 8集成OpenID Connect(Ping Federate)遇远程登录失败求助

问题分析与完整实现方案

核心问题梳理

  1. 纯API场景错误使用交互式OIDC(Cookie+OIDC)配置:API无前端页面,无法处理登录跳转后的回调,导致状态参数异常、认证失败
  2. 代码存在语法错误(括号未闭合、c.type大小写错误、变量未定义)
  3. 回调路径配置为带[Authorize]的接口,认证回调无法正常触发

方案一:纯API场景(推荐)- JWT Bearer认证

此方案适用于无前端页面的API,由客户端(如Postman、前端应用)先向Ping Federate获取AccessToken,再通过Authorization: Bearer <token>请求API。

1. Program.cs完整配置

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

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

// 配置JWT Bearer认证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "https://my-idp-website.com"; // Ping Federate的OIDC元数据地址,框架会自动拼接/.well-known/openid-configuration
        options.Audience = "17990c36-59f3-4d345cd57D45"; // 与Ping Federate中配置的ClientId一致
        options.ClientSecret = "some secret key"; // Ping Federate用非对称加密时可省略,框架会自动从元数据获取公钥

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            NameClaimType = "name",
            RoleClaimType = "role"
        };

        options.Events = new JwtBearerEvents
        {
            OnTokenValidated = context =>
            {
                var nameIdentifier = context.Principal?.Claims.FirstOrDefault(c => c.Type.EndsWith("nameidentifier"))?.Value;
                // 自定义逻辑:如记录日志、添加自定义Claim
                return Task.CompletedTask;
            },
            OnAuthenticationFailed = context =>
            {
                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                return Task.CompletedTask;
            }
        };
    });

builder.Services.AddAuthorization();

var app = builder.Build();

// 中间件顺序必须严格遵守
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

2. AuthController修正代码

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

[ApiController]
[Route("/api/auth")]
public class AuthController : ControllerBase
{
    private readonly ILogger<AuthController> _logger;

    public AuthController(ILogger<AuthController> logger)
    {
        _logger = logger;
    }

    [Authorize]
    [HttpGet]
    public IActionResult Get()
    {
        _logger.LogInformation("Authenticated request received");
        
        var nameIdentifier = User.Claims.FirstOrDefault(c => c.Type.EndsWith("nameidentifier"))?.Value ?? "Unknown";
        var email = User.Claims.FirstOrDefault(c => c.Type == "email")?.Value;

        return Ok(new
        {
            NameIdentifier = nameIdentifier,
            Email = email,
            AllClaims = User.Claims.Select(c => new { c.Type, c.Value })
        });
    }
}

方案二:API需触发交互式OIDC认证(特殊场景)

如果API必须触发Ping Federate登录页面跳转,需修正OIDC配置,确保回调路径允许匿名,并修复状态参数问题:

1. Program.cs完整配置

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

// 配置Cookie+OIDC认证
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.ResponseMode = OpenIdConnectResponseMode.FormPost;
    options.Authority = "https://my-idp-website.com";
    options.ClientId = "17990c36-59f3-4d345cd57D45";
    options.ClientSecret = "some secret key";
    options.ResponseType = OpenIdConnectResponseType.Code; // 推荐使用Code Flow
    options.CallbackPath = "/api/auth/callback"; // 单独的匿名回调路径
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("email");
    options.Scope.Add("phone");
    options.Scope.Add("profile");

    options.Events = new OpenIdConnectEvents
    {
        OnTokenValidated = context =>
        {
            var nameIdentifier = context.Principal?.Claims.FirstOrDefault(c => c.Type.EndsWith("nameidentifier"))?.Value;
            return Task.CompletedTask;
        },
        OnRemoteFailure = context =>
        {
            // 修复状态为空问题:保留错误信息
            context.Response.Redirect($"/api/auth/error?error={Uri.EscapeDataString(context.Failure.Message)}");
            context.HandleResponse();
            return Task.CompletedTask;
        }
    };
});

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

2. AuthController修正代码(含回调和错误处理)

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

[ApiController]
[Route("/api/auth")]
public class AuthController : ControllerBase
{
    private readonly ILogger<AuthController> _logger;

    public AuthController(ILogger<AuthController> logger)
    {
        _logger = logger;
    }

    [Authorize]
    [HttpGet]
    public IActionResult Get()
    {
        _logger.LogInformation("Authenticated request received");
        
        var nameIdentifier = User.Claims.FirstOrDefault(c => c.Type.EndsWith("nameidentifier"))?.Value ?? "Unknown";
        return Ok(new { NameIdentifier = nameIdentifier, AllClaims = User.Claims.Select(c => new { c.Type, c.Value }) });
    }

    // OIDC回调端点:允许匿名
    [AllowAnonymous]
    [HttpGet("callback")]
    public IActionResult Callback()
    {
        return Redirect("/api/auth");
    }

    // 错误处理端点
    [AllowAnonymous]
    [HttpGet("error")]
    public IActionResult Error(string error)
    {
        return BadRequest(new { ErrorMessage = error });
    }
}

关键注意事项

  • Ping Federate端配置:确保ClientId、ClientSecret、回调路径与代码一致,且已启用对应Scope(openid、email等)
  • 中间件顺序:UseAuthentication()必须在UseAuthorization()之前
  • 纯API场景优先使用JWT Bearer:符合RESTful无状态设计规范,避免Cookie带来的冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:33:16