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

ASP.NET Core集成MS Identity与OIDC时OIDC登出失效问题

问题描述

现有遗留方案通过Microsoft Identity对接本地SQL库实现用户认证,现需访问由Open ID Connect(OIDC)保护的第三方API,其余功能正常,但用户登出API时,OIDC登出流程未触发(未跳转至OIDC登出页面)。

使用的Startup.cs代码片段如下:

public void ConfigureServices(IServiceCollection services)
{
  services.AddIdentity<AppUser, IdentityRole>()
          .AddEntityFrameworkStores<AppIdentityDbContext>()
          .AddDefaultTokenProviders();

  services.ConfigureApplicationCookie(options =>
  {
    options.LoginPath = "/Account/LogIn";
    options.LogoutPath = "/Account/LogOut";
    options.AccessDeniedPath = "/Account/AccessDenied";
  });
     
  services.AddAuthentication(options =>
  {
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; // "Cookies"
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; // "OpenIdConnect"
  })
  .AddJwtBearer(options => 
  {
    options.TokenValidationParameters = new TokenValidationParameters
    {
      ValidateIssuer = true,
      ValidateAudience = true,
      ValidateLifetime = true,
      ValidateIssuerSigningKey = true,
      ValidIssuer = Configuration["Jwt:Issuer"],
      ValidAudience = Configuration["Jwt:Issuer"],
      IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
    };
  })
  .AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime))
  .AddOpenIdConnect(options =>
  {
    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.Authority = identityUrl;
    options.ClientId = clientId;
    options.ClientSecret = clientSecret;
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.UsePkce = true;
    options.RequireHttpsMetadata = false;
    options.Scope.Clear();
    options.Scope.Add("openid");
    options.SignedOutRedirectUri = "/Home/Index";
  });
}

public async Task Login()
{
  await HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme); // "OpenIdConnect"
}

public async Task Logout()
{
  await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); // "OpenIdConnect"
  //await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); // "Cookies"
}

当前使用Microsoft.AspNetCore.Authentication.OpenIdConnect 5.0.17版本,尝试开启IdentityModelEventSource.ShowPII未生效,搭建仅含OIDC认证的示例应用通过Wireshark抓包,但因HTTPS无法解密流量。

需明确两个问题:

  • OIDC登出失效的原因是什么?
  • 这种双重认证场景是否可行?
问题分析与解决方案

OIDC登出失效的原因

  1. Cookie认证方案冲突
    代码中同时调用services.AddIdentity()和services.AddAuthentication().AddCookie(),导致注册了两个同名的Cookies认证方案。AddIdentity()内部已默认注册Identity应用Cookie,手动再调用AddCookie()会造成覆盖或关联混乱,使得OIDC登出时无法正确匹配到对应的本地Cookie,进而无法触发跳转至OIDC提供商的登出流程。

  2. 登出逻辑不完整
    现有Logout方法仅调用SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme),但OIDC登出需要先清除本地认证Cookie,再触发OIDC提供商的登出端点跳转。缺少本地Cookie清除步骤,会导致OIDC登出流程无法正常触发。

  3. OIDC配置缺失关键参数
    现有OIDC配置未设置PostLogoutRedirectUri(部分OIDC提供商要求该参数确认登出后的跳转地址),也未配置SignedOutCallbackPath及相关事件处理登出后的回调逻辑,影响登出流程的完整性。

双重认证场景的可行性

这种混合认证场景(本地Identity + OIDC + JWT Bearer)完全可行,是遗留系统对接外部认证资源的常见方案。只要正确配置各认证方案的优先级、名称关联关系,就能实现本地用户认证、OIDC第三方登录/登出、JWT API授权的共存。

修复步骤

  1. 移除重复的Cookie认证注册
    删除services.AddAuthentication().AddCookie(...)代码行,通过ConfigureApplicationCookie统一配置Identity应用Cookie的过期时间:

    services.ConfigureApplicationCookie(options =>
    {
      options.LoginPath = "/Account/LogIn";
      options.LogoutPath = "/Account/LogOut";
      options.AccessDeniedPath = "/Account/AccessDenied";
      options.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime); // 在此设置Cookie过期时间
    });
    
  2. 完善登出逻辑
    修改Logout方法,同时清除本地Cookie并触发OIDC登出跳转:

    public async Task Logout()
    {
      // 清除本地Identity认证Cookie
      await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
      // 触发OIDC提供商登出,并指定跳转地址
      await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties
      {
        RedirectUri = "/Home/Index"
      });
    }
    
  3. 补充OIDC登出相关配置
    在AddOpenIdConnect配置中添加以下参数:

    options.PostLogoutRedirectUri = "/Home/Index";
    options.SignedOutCallbackPath = "/Account/SignedOut"; // 用于处理OIDC登出后的回调
    options.Events.OnSignedOutCallbackRedirect = context =>
    {
      context.Response.Redirect(context.Options.SignedOutRedirectUri);
      context.HandleResponse();
      return Task.CompletedTask;
    };
    
  4. 正确开启PII日志排查
    在Program.cs(或Startup的Configure方法开头)添加代码开启PII日志,并调整日志级别配置:

    IdentityModelEventSource.ShowPII = true;
    

    同时在appsettings.json中配置日志级别:

    "Logging": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft.AspNetCore": "Warning",
        "Microsoft.AspNetCore.Authentication": "Debug"
      }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 02:44:54