Asignación, personalización y transformación de notificaciones en ASP.NET Core

De Damien Bowden

Las notificaciones se pueden crear a partir de cualquier usuario o datos de identidad que se puedan emitir mediante un proveedor de identidades de confianza o de una identidad de ASP.NET Core. Una notificación es un par de valor y nombre que representa lo que es el sujeto, no lo que puede hacer. En este artículo se detallan las siguientes áreas:

  • Configuración y asignación de notificaciones mediante un cliente de OpenID Connect
  • Establecimiento del nombre y la notificación de rol
  • Restablecimiento de los espacios de nombres de notificaciones
  • Personalización, ampliación de las notificaciones con TransformAsync

Asignación de notificaciones mediante la autenticación de OpenID Connect

Las notificaciones de perfil se pueden devolver en id_token, que se devuelve después de una autenticación correcta. La aplicación cliente ASP.NET Core solo requiere el ámbito del perfil. Cuando se usa id_token para notificaciones, no se requiere ninguna asignación de notificaciones adicional.

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

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

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

app.MapRazorPages();

app.Run();

El código anterior requiere el paquete NuGet Microsoft.AspNetCore.Authentication.OpenIdConnect.

Otra manera de obtener las notificaciones del usuario es usar la API de información de usuario de OpenID Connect. La aplicación cliente ASP.NET Core usa la propiedad GetClaimsFromUserInfoEndpoint para configurarlo. Una diferencia importante de la primera configuración es que debe especificar las notificaciones que necesita mediante el método MapUniqueJsonKey; de lo contrario, solo las notificaciones estándar name, given_name y email estarán disponibles en la aplicación cliente. Las notificaciones incluidas en id_token se asignan por valor predeterminado. Esta es la principal diferencia con la primera opción. Debe definir explícitamente algunas de las notificaciones que necesite.

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

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
       options.GetClaimsFromUserInfoEndpoint = true;
       options.ClaimActions.MapUniqueJsonKey("preferred_username",
                                             "preferred_username");
       options.ClaimActions.MapUniqueJsonKey("gender", "gender");
   });

var app = builder.Build();

// Code removed for brevity.

Asignación de notificaciones de nombre y notificación de rol

La notificación Name y la notificación Role se asignan a las propiedades predeterminadas en el contexto HTTP de ASP.NET Core. A veces es necesario usar diferentes notificaciones para las propiedades predeterminadas, o la notificación de nombre y la notificación de rol no coinciden con los valores predeterminados. Las notificaciones se pueden asignar mediante la propiedad TokenValidationParameters y establecerse en cualquier notificación según sea necesario. Los valores de las notificaciones se pueden usar directamente en la propiedad HttpContext User.Identity.Name y los roles.

Si User.Identity.Name no tiene ningún valor o faltan los roles, compruebe los valores de las notificaciones devueltas y establezca NameClaimType y los valores RoleClaimType. Las notificaciones devueltas de la autenticación de cliente se pueden ver en el contexto HTTP.

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
  .AddCookie()
  .AddOpenIdConnect(options =>
  {
       // Other options...
       options.TokenValidationParameters = new TokenValidationParameters
       {
          NameClaimType = "email"
          //, RoleClaimType = "role"
       };
  });

Espacios de nombres de notificaciones, espacios de nombres predeterminados

ASP.NET Core agrega espacios de nombres predeterminados a algunas notificaciones conocidas, lo que podría no ser necesario en la aplicación. Opcionalmente, deshabilite estos espacios de nombres agregados y use las notificaciones exactas que creó el servidor OpenID Connect.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear();

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });

var app = builder.Build();

// Code removed for brevity.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });

var app = builder.Build();

// Code removed for brevity.

Si necesita deshabilitar los espacios de nombres por esquema y no globalmente, puede usar la opción MapInboundClaims = false.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.MapInboundClaims = false;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });

var app = builder.Build();

// Code removed for brevity.

Extensión o adición de notificaciones personalizadas con IClaimsTransformation

La interfaz IClaimsTransformation se puede usar para agregar notificaciones adicionales a la clase ClaimsPrincipal. La interfaz requiere un único método TransformAsync. Este método puede llamarse varias veces. Agregue solo una nueva notificación si aún no existe en ClaimsPrincipal. ClaimsIdentity se crea para agregar las nuevas notificaciones y se puede agregar a ClaimsPrincipal.

using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;

public class MyClaimsTransformation : IClaimsTransformation
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        ClaimsIdentity claimsIdentity = new ClaimsIdentity();
        var claimType = "myNewClaim";
        if (!principal.HasClaim(claim => claim.Type == claimType))
        {
            claimsIdentity.AddClaim(new Claim(claimType, "myClaimValue"));
        }

        principal.AddIdentity(claimsIdentity);
        return Task.FromResult(principal);
    }
}

La interfaz IClaimsTransformation y la clase MyClaimsTransformation se pueden registrar como un servicio:

builder.Services.AddTransient<IClaimsTransformation, MyClaimsTransformation>();

Asignación de notificaciones de proveedores de identidades externos

Consulte el documento siguiente:

Conservar notificaciones y tokens adicionales de proveedores externos en ASP.NET Core

Las notificaciones se pueden crear a partir de cualquier usuario o datos de identidad que se puedan emitir mediante un proveedor de identidades de confianza o de una identidad de ASP.NET Core. Una notificación es un par de valor y nombre que representa lo que es el sujeto, no lo que puede hacer. En este artículo se detallan las siguientes áreas:

  • Configuración y asignación de notificaciones mediante un cliente de OpenID Connect
  • Establecimiento del nombre y la notificación de rol
  • Restablecimiento de los espacios de nombres de notificaciones
  • Personalización, ampliación de las notificaciones con TransformAsync

Asignación de notificaciones mediante la autenticación de OpenID Connect

Las notificaciones de perfil se pueden devolver en id_token, que se devuelve después de una autenticación correcta. La aplicación cliente ASP.NET Core solo requiere el ámbito del perfil. Cuando se usa id_token para notificaciones, no se requiere ninguna asignación de notificaciones adicional.

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });

Otra manera de obtener las notificaciones del usuario es usar la API de información de usuario de OpenID Connect. La aplicación cliente ASP.NET Core usa la propiedad GetClaimsFromUserInfoEndpoint para configurarlo. Una diferencia importante de la primera configuración es que debe especificar las notificaciones que necesita mediante el método MapUniqueJsonKey; de lo contrario, solo las notificaciones estándar name, given_name y email estarán disponibles en la aplicación cliente. Las notificaciones incluidas en id_token se asignan por valor predeterminado. Esta es la principal diferencia con la primera opción. Debe definir explícitamente algunas de las notificaciones que necesite.

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
       options.GetClaimsFromUserInfoEndpoint = true;
       options.ClaimActions.MapUniqueJsonKey("preferred_username", "preferred_username");
       options.ClaimActions.MapUniqueJsonKey("gender", "gender");
   }); 

Asignación de notificaciones de nombre y notificación de rol

La notificación Name y la notificación Role se asignan a las propiedades predeterminadas en el contexto HTTP de ASP.NET Core. A veces es necesario usar diferentes notificaciones para las propiedades predeterminadas, o la notificación de nombre y la notificación de rol no coinciden con los valores predeterminados. Las notificaciones se pueden asignar mediante la propiedad TokenValidationParameters y establecerse en cualquier notificación según sea necesario. Los valores de las notificaciones se pueden usar directamente en la propiedad HttpContext User.Identity.Name y los roles.

Si User.Identity.Name no tiene ningún valor o faltan los roles, compruebe los valores de las notificaciones devueltas y establezca NameClaimType y los valores RoleClaimType. Las notificaciones devueltas de la autenticación de cliente se pueden ver en el contexto HTTP.

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       // other options...
       options.TokenValidationParameters = new TokenValidationParameters
       {
         NameClaimType = "email", 
         // RoleClaimType = "role"
       };
   });

Espacios de nombres de notificaciones, espacios de nombres predeterminados

ASP.NET Core agrega espacios de nombres predeterminados a algunas notificaciones conocidas, lo que podría no ser necesario en la aplicación. Opcionalmente, deshabilite estos espacios de nombres agregados y use las notificaciones exactas que creó el servidor OpenID Connect.

public void Configure(IApplicationBuilder app)
{
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

Extensión o adición de notificaciones personalizadas con IClaimsTransformation

La interfaz IClaimsTransformation se puede usar para agregar notificaciones adicionales a la clase ClaimsPrincipal. La interfaz requiere un único método TransformAsync. Este método puede llamarse varias veces. Agregue solo una nueva notificación si aún no existe en ClaimsPrincipal. ClaimsIdentity se crea para agregar las nuevas notificaciones y se puede agregar a ClaimsPrincipal.

public class MyClaimsTransformation : IClaimsTransformation
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
       ClaimsIdentity claimsIdentity = new ClaimsIdentity();
       var claimType = "myNewClaim";
       if (!principal.HasClaim(claim => claim.Type == claimType))
       {		   
          claimsIdentity.AddClaim(new Claim(claimType, "myClaimValue"));
       }

       principal.AddIdentity(claimsIdentity);
       return Task.FromResult(principal);
    }
}

La interfaz IClaimsTransformation y la clase MyClaimsTransformation se pueden agregar en el método ConfigureServices como servicio.

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IClaimsTransformation, MyClaimsTransformation>();

Extender o agregar notificaciones personalizadas en ASP.NET Core Identity

Consulte el documento siguiente:

Adición de notificaciones a Identity con IUserClaimsPrincipalFactory

Asignación de notificaciones de proveedores de identidades externos

Consulte el documento siguiente:

Conservar notificaciones y tokens adicionales de proveedores externos en ASP.NET Core