Сопоставление, настройка и преобразование утверждений в ASP.NET Core

Дэмиен Боуден

Утверждения можно создавать на основе любых пользовательских или идентификационных данных, которые могут выдаваться с помощью доверенного поставщика удостоверений или ASP.NET Core Identity. Утверждение — это пара «имя — значение», которая описывает, чем является субъект, а не то, что субъект может делать.

В этой статье описывается настройка и сопоставление утверждений с помощью клиента OpenID Connect и выполнение следующих задач.

  • Настройка утверждений имени и роли
  • Сброс пространств имен утверждений
  • Настройка и расширение утверждений TransformAsync с помощью метода

Сопоставление утверждений с проверкой подлинности OpenID Connect

Утверждения профиля можно вернуть в id_tokenобъекте, который возвращается после успешной аутентификации. Для клиентского приложения ASP.NET Core требуется только область профиля. Если вы используете id_token для утверждений, дополнительное сопоставление утверждений не требуется.

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();

Для предыдущего кода требуется пакет NuGet Microsoft.AspNetCore.Authentication.OpenIdConnect .

Другим способом получения утверждений пользователя является использование API сведений о пользователе OpenID Connect. Клиентское приложение ASP.NET Core использует свойство GetClaimsFromUserInfoEndpoint для настройки. В этом сценарии необходимо явно указать необходимые утверждения с помощью MapUniqueJsonKey метода. В противном случае в клиентском приложении доступны только стандартные утверждения name, given_name и email. Утверждения, включенные в список id_token , сопоставляются по умолчанию.

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.

Примечание.

По умолчанию обработчик Open ID Connect использует запросы авторизации по протоколу PAR, если документ обнаружения поставщика удостоверений заявляет о поддержке PAR. Обычное расположение документа обнаружения поставщика удостоверений — каталог .well-known/openid-configuration. Если вы не можете использовать PAR в конфигурации клиента у поставщика удостоверений, PAR можно отключить с помощью параметра PushedAuthorizationBehavior.

builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect("oidc", oidcOptions =>
    {
        // Other provider-specific configuration goes here.

        // The default value is PushedAuthorizationBehavior.UseIfAvailable.

        // 'OpenIdConnectOptions' does not contain a definition for 'PushedAuthorizationBehavior'
        // and no accessible extension method 'PushedAuthorizationBehavior' accepting a first argument
        // of type 'OpenIdConnectOptions' could be found
        oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Disable;
    });

Чтобы убедиться, что проверка подлинности выполняется только в том случае, если используется PAR, используйте вместо него перечисление PushedAuthorizationBehavior.Require . Это изменение также добавляет в OpenIdConnectEvents новое событие OnPushAuthorization, которое можно использовать для настройки передаваемого запроса авторизации или его обработки вручную. Дополнительные сведения см. в предложении API в GitHub dotnet/aspnetcore issue #51686 - Support for Pushed Authorization Requests in OidcHandler.

Сопоставление заявлений об именах и ролях

Утверждения name и role сопоставляются со свойствами по умолчанию в контексте HTTP ASP.NET Core. Иногда необходимо использовать различные утверждения для свойств по умолчанию, а утверждение имени и утверждение роли не совпадают со значениями по умолчанию. Утверждения можно сопоставить с помощью TokenValidationParameters свойства и задать любое утверждение по мере необходимости. Значения из утверждений можно использовать непосредственно в свойстве HttpContext User.Identity.Name и ролях.

Если у свойства User.Identity.Name нет значения или роли отсутствуют, проверьте значения в возвращённых утверждениях и задайте значения NameClaimType и RoleClaimType. Возвращенные утверждения из проверки подлинности клиента можно просмотреть в контексте 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"
       };
  });

Пространства имен утверждений, пространства имен по умолчанию

ASP.NET Core добавляет пространства имен по умолчанию в некоторые известные утверждения. Стандартные утверждения могут не понадобиться в приложении. При необходимости можно отключить добавленные пространства имён и использовать исходные утверждения, созданные сервером 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.

Если необходимо отключить пространства имен для каждой схемы, а не глобально, можно использовать этот 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.

Расширение или добавление настраиваемых утверждений с помощью IClaimsTransformation

Интерфейс IClaimsTransformation можно использовать для добавления дополнительных утверждений в ClaimsPrincipal класс. Для интерфейса требуется один метод TransformAsync. Этот метод может вызываться несколько раз. Добавляйте новое утверждение только если оно еще не существует в ClaimsPrincipal. Объект ClaimsIdentity создается для добавления новых утверждений и его можно добавить в .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);
    }
}

Интерфейс IClaimsTransformation и MyClaimsTransformation класс можно зарегистрировать как службу:

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

Сопоставление утверждений от внешних поставщиков удостоверений

Если вы хотите сопоставить утверждения от внешних поставщиков удостоверений для своего приложения, см. раздел Сохранение других утверждений и токенов от внешних поставщиков в ASP.NET Core.

Утверждения можно создавать из любых данных пользователя или данных удостоверения личности, которые могут быть созданы с помощью доверенного поставщика удостоверений или удостоверений ASP.NET Core. Утверждение — это пара «имя-значение», которая описывает, кем является субъект, а не то, что он может делать. В этой статье рассматриваются следующие области:

  • Настройка и сопоставление утверждений с помощью клиента OpenID Connect
  • Установите утверждение имени и роли
  • Сброс пространств имен утверждений
  • Настройка, расширение утверждений с помощью TransformAsync

Сопоставление утверждений с помощью проверки подлинности OpenID Connect

Утверждения профиля можно вернуть в id_tokenобъекте, который возвращается после успешной аутентификации. Для клиентского приложения ASP.NET Core требуется только область профиля. При использовании id_token для утверждений не требуется дополнительное сопоставление утверждений.

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;
   });

Другим способом получения утверждений пользователя является использование API сведений о пользователе OpenID Connect. Клиентское приложение ASP.NET Core использует GetClaimsFromUserInfoEndpoint свойство для настройки этого свойства. Одно из важных отличий от первых настроек заключается в том, что необходимо указать требуемые утверждения, используя метод MapUniqueJsonKey, в противном случае в клиентском приложении будут доступны только стандартные утверждения name, given_name и email. Утверждения, включенные в список id_token , сопоставляются по умолчанию. Это основное отличие от первого варианта. Необходимо явно определить некоторые необходимые утверждения.

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");
   }); 

Сопоставление заявлений об именах и ролях

Утверждения Name и Role сопоставляются со свойствами по умолчанию в контексте HTTP ASP.NET Core. Иногда требуется использовать различные утверждения для свойств по умолчанию, или утверждения имени и роли не совпадают со значениями по умолчанию. Утверждения можно сопоставить с помощью свойства TokenValidationParameters и задать любое утверждение по мере необходимости. Значения из утверждений можно использовать непосредственно в HttpContext User.Identity.Name и роли.

User.Identity.Name Если у User.Identity.Name нет значения или отсутствуют роли, проверьте значения в возвращённых требованиях и задайте значения для RoleClaimType и . Возвращенные утверждения из проверки подлинности клиента можно просмотреть в контексте 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"
       };
   });

Пространства имен утверждений, пространства имен по умолчанию

ASP.NET Core добавляет пространства имен по умолчанию в некоторые известные утверждения, которые могут не потребоваться в приложении. При необходимости отключите эти добавленные пространства имен и используйте точные утверждения, созданные сервером OpenID Connect.

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

Расширение или добавление настраиваемых утверждений с помощью IClaimsTransformation

Интерфейс IClaimsTransformation можно использовать для добавления дополнительных утверждений в ClaimsPrincipal класс. Для интерфейса требуется один метод TransformAsync. Этот метод может вызываться несколько раз. Добавьте новое утверждение только в том случае, если оно еще не существует в ClaimsPrincipal. Создается ClaimsIdentity для добавления новых утверждений, и его можно добавить в 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);
    }
}

Интерфейс IClaimsTransformation и MyClaimsTransformation класс можно добавить в метод ConfigureServices в качестве службы.

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

Расширение или добавление пользовательских утверждений в ASP.NET Core Identity

См. следующий документ:

Добавление утверждений в Identity с использованием IUserClaimsPrincipalFactory

Сопоставление утверждений от внешних поставщиков удостоверений

См. следующий документ:

Сохранение дополнительных утверждений и маркеров от внешних поставщиков в ASP.NET Core