转换ASP.Net Core中的Open Id Connect声明

本文关键字:Id Connect 声明 Open 中的 ASP Net Core 转换 | 更新日期: 2023-09-27 18:03:17

我正在编写一个ASP.Net核心Web应用程序,并使用UseOpenIdConnectAuthentication将其连接到IdentityServer3。模拟他们的ASP.Net MVC 5示例,我试图转换从Identity Server接收回来的声明,以删除"肯定不需要的低级别协议声明"。在MVC 5中,他们添加了一个SecurityTokenValidated Notification的处理程序,该处理程序将AuthenticationTicket换成只需要声明的声明。

在ASP.Net Core中,为了实现等效功能,我认为需要在OpenIdConnectEvents中处理OnTokenValidated。然而,在那个阶段,似乎没有检索到额外的范围信息。如果我处理OnUserInformationReceived,则存在额外的信息,但存储在用户而不是主体上。

在身份验证完成后,其他事件似乎都不是永久删除我不想保留的声明的明显位置。如有任何建议,不胜感激!

转换ASP.Net Core中的Open Id Connect声明

我喜欢LeastPrivilege关于在流程早期进行转换的建议。提供的代码不太有效。这个版本做:

var oidcOptions = new OpenIdConnectOptions
{
   ...
   Events = new OpenIdConnectEvents
   {
       OnTicketReceived = e =>
       {
          e.Principal = TransformClaims(e.Ticket.Principal);
          return Task.CompletedTask;
       }
   }
};

这将取代Principal而不是Ticket。您可以使用我另一个答案中的代码来创建新的Principal。你也可以同时更换Ticket,但我不确定是否有必要。

因此,感谢LeastPrivilege和Adem提出的几乎可以回答我问题的方法。。。只是代码需要稍作调整。总的来说,我更喜欢LeastPrivilege关于尽早转换索赔的建议。

您可以实现SignInSchemeOnSigningIn事件。这里有一个例子:

        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            AuthenticationScheme = "OpenIdCookies",
            AutomaticAuthenticate = true,
            Events = new CookieAuthenticationEvents()
            {
                OnSigningIn = async (context) =>
                {
                    ClaimsIdentity identity = (ClaimsIdentity)context.Principal.Identity;
                    identity.Claims = identity.Claims.Where(...);
                }
            }
        });
        var oidcOptions = new OpenIdConnectOptions
        {
            AuthenticationScheme = "oidc",
            SignInScheme = "OpenIdCookies"
        };
        //.. set other options
        app.UseOpenIdConnectAuthentication(oidcOptions); 

感谢Adem的回复。。。它解决了绝大多数问题。。。唯一的问题是身份。声明是只读属性。我发现创建一个新的校长确实有效:

Events = new CookieAuthenticationEvents()
{
    OnSigningIn = (context) =>
    {
        ClaimsIdentity identity = (ClaimsIdentity)context.Principal.Identity;
        var givenName = identity.FindFirst(Constants.ClaimTypes.GivenName);
        var familyName = identity.FindFirst(Constants.ClaimTypes.FamilyName);
        var sub = identity.FindFirst(Constants.ClaimTypes.Subject);
        var claimsToKeep = new List<Claim> {givenName, familyName, sub};
        var newIdentity = new ClaimsIdentity(claimsToKeep, identity.AuthenticationType);
        context.Principal = new ClaimsPrincipal(newIdentity);
        return Task.FromResult(0);
    }
}

我不确定这是否是正确的方法,但它似乎有效。

我个人更喜欢在进行实际身份验证的中间件中进行声明转换。

您可以在OIDC中间件上使用OnTicketReceived事件。

var oidcOptions = new OpenIdConnectOptions
{
   AuthenticationScheme = "oidc",
   SignInScheme = "cookies",
   Authority = Clients.Constants.BaseAddress,
   ClientId = "mvc.hybrid",
   ClientSecret = "secret",
   ResponseType = "code id_token",
   SaveTokens = true,
   TokenValidationParameters = new TokenValidationParameters
   {
      NameClaimType = JwtClaimTypes.Name,
      RoleClaimType = JwtClaimTypes.Role,
   },
   Events = new OpenIdConnectEvents
   {
       OnTicketReceived = e =>
       {
           ClaimsPrincipal p = TransformClaims(e.Ticket.Principal);
           e.Ticket = new AuthenticationTicket(
            p,
            e.Ticket.Properties,
            e.Ticket.AuthenticationScheme);
        return Task.CompletedTask;
    }
  }
};

多亏了这个线程中的答案,我自己也能做到这一点。这里没有解决的问题是,如果需要修改索赔,则需要服务。在我的案例中,我需要构建服务提供商,以便获得正确的依赖关系,从而转换声明。(我还可以在不进行转换的情况下删除声明——在这里也显示了代码(。

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddAuthentication(options =>
        {
            // set options
        })
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
        {
            // set options
        })
        .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
        {
            // options such as Authority, ClientId, etc set here
            options.Authority = "your-value";
            options.ClientId = "your-value";
            // ...
            // remove automatically mapped claims we do not need, keeps the authentication cookie smaller
            options.ClaimActions.DeleteClaim("sid");
            options.ClaimActions.DeleteClaim("idp");
            options.ClaimActions.DeleteClaim("s_hash");
            options.ClaimActions.DeleteClaim("auth_time");
            options.Events.OnTicketReceived = async context =>
            {
                // Build the service provider and necessary dependencies
                // in order to enhance our claims once we receive it initially
                ServiceProvider serviceProvider = services.BuildServiceProvider();
                ICustomProvider customProvider = serviceProvider.GetService<ICustomProvider>();
                EnhanceClaimsTransformation claimsTransformation = new EnhanceClaimsTransformation(customProvider);
                context.Principal = await claimsTransformation.TransformAsync(context.Principal);
                await Task.CompletedTask;
            };
        });
}

增强索赔转换(ICustomProvider在ConfigureServices中的依赖项注入中注册(

请注意,在这段代码中,我们需要对主体进行克隆,以实际向其添加声明。

public class EnhanceClaimsTransformation : IClaimsTransformation
{
    private readonly ICustomProvider _customProvider;
    public EnhanceClaimsTransformation(ICustomProvider customProvider)
    {
        _customProvider = customProvider;
    }
    /// <summary>
    /// Upon authentication, we transform the claims in order to enhance
    /// the claims with user-enhanced values.
    /// </summary>
    /// <param name="principal"></param>
    /// <returns></returns>
    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        // https://gunnarpeipman.com/aspnet-core-adding-claims-to-existing-identity/
        ClaimsPrincipal clone = principal.Clone();
        ClaimsIdentity claimsIdentity = (ClaimsIdentity)clone.Identity;
        Response response = await _customProvider.Find(principal.Identity.Name, CancellationToken.None);
        // Setting claims values
        claimsIdentity.AddClaims(new List<Claim>
        {
            new Claim("Datapoint1", response.Datapoint1),
            new Claim("Datapoint2", response.Datapoint2),
            // ...
        });        
        return clone;
    }
}