Mapeamento, personalização e transformação de declarações no ASP.NET Core
Por Damien Bowden
As declarações podem ser criadas a partir de qualquer dado de usuário ou identity que possa ser emitido usando um provedor de identity confiável ou identity do ASP.NET Core. Uma declaração é um par de valores de nome que representa o titular, não o que o titular pode fazer. Esse artigo aborda os seguintes tópicos:
- Como configurar e mapear declarações usando um cliente do OpenID Connect
- Definir o nome e a declaração de função
- Redefinir os namespaces de declarações
- Personalizar, estender as declarações por meio de TransformAsync
Como mapear declarações usando a autenticação do OpenID Connect
As declarações de perfil podem ser retornadas no id_token
, que é retornado após uma autenticação bem-sucedida. O aplicativo cliente do ASP.NET Core requer apenas o escopo do perfil. Ao usar o id_token
para declarações, nenhum mapeamento de declarações extra é necessário.
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();
O código anterior requer o pacote NuGet Microsoft.AspNetCore.Authentication.OpenIdConnect .
Outra maneira de obter as declarações do usuário é usar a API de Informações do Usuário do OpenID Connect. O aplicativo cliente do ASP.NET Core usa a propriedade GetClaimsFromUserInfoEndpoint
para configurar isso. Uma diferença importante em relação às primeiras configurações é que você deve especificar as declarações necessárias por meio do método MapUniqueJsonKey
, caso contrário, somente as declarações padrão name
, given_name
e email
estarão disponíveis no aplicativo cliente. As declarações incluídas no id_token
são mapeadas por padrão. Essa é a principal diferença para a primeira opção. Você deve definir explicitamente algumas das declarações necessárias.
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.
Observação
O manipulador padrão do Open ID Connect usa PAR (Solicitações de Autorização por Push) se o documento de descoberta do provedor de identity anunciar suporte para PAR. O documento de descoberta do provedor de identity geralmente é encontrado em .well-known/openid-configuration
. Se você não puder usar o PAR na configuração do cliente no provedor de identity, o PAR poderá ser desabilitado usando a opção 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;
});
Para garantir que a autenticação só seja bem-sucedida se o PAR for usado, use PushedAuthorizationBehavior.Require. Essa alteração também introduz um novo evento OnPushAuthorization para OpenIdConnectEvents, que pode ser usado para personalizar a solicitação de autorização por push ou tratá-la manualmente. Para ver mais detalhes, consulte a proposta da API.
Mapeamento de declaração de nome e declaração de função
A declaração Nome e a declaração Função são mapeadas para propriedades padrão no contexto HTTP ASP.NET Core. Às vezes, é necessário usar declarações diferentes para as propriedades padrão ou a declaração de nome e a declaração de função não correspondem aos valores padrão. As declarações podem ser mapeadas usando a propriedade TokenValidationParameters e definidas como qualquer declaração conforme necessário. Os valores das declarações podem ser usados diretamente na propriedade User.Identity.Name e nas funções.
Se o User.Identity.Name
não tiver nenhum valor ou as funções estiverem ausentes, verifique os valores nas declarações retornadas e defina os valores NameClaimType
e RoleClaimType
. As declarações retornadas da autenticação do cliente podem ser exibidas no 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"
};
});
Namespaces de declarações, namespaces padrão
O ASP.NET Core adiciona namespaces padrão a algumas declarações conhecidas, o que pode não ser necessário no aplicativo. Opcionalmente, desabilite esses namespaces adicionados e use as declarações exatas que o servidor OpenID Connect criou.
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.
Se você precisar desabilitar os namespaces por esquema e não globalmente, poderá usar a opção 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.
Estender ou adicionar declarações personalizadas por meio de IClaimsTransformation
A interface IClaimsTransformation pode ser usada para adicionar declarações extras à classe ClaimsPrincipal. A interface exige um único método TransformAsync. Esse método pode ser chamado várias vezes. Só adicione uma nova declaração se ela ainda não existir no ClaimsPrincipal
. Um ClaimsIdentity
é criado para adicionar as novas declarações e isso pode ser adicionado ao 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);
}
}
A interface IClaimsTransformation e a classe MyClaimsTransformation
podem ser registradas como um serviço:
builder.Services.AddTransient<IClaimsTransformation, MyClaimsTransformation>();
Mapear declarações de provedores de identity externos
Consulte a seguinte documentação:
Para saber mais, consulte Persistir declarações e tokens adicionais de provedores externos no ASP.NET Core.
As declarações podem ser criadas a partir de qualquer dado de usuário ou identity que possa ser emitido usando um provedor de identity confiável ou identity do ASP.NET Core. Uma declaração é um par de valores de nome que representa o titular, não o que o titular pode fazer. Esse artigo aborda os seguintes tópicos:
- Como configurar e mapear declarações usando um cliente do OpenID Connect
- Definir o nome e a declaração de função
- Redefinir os namespaces de declarações
- Personalizar, estender as declarações por meio de TransformAsync
Como mapear declarações usando a autenticação do OpenID Connect
As declarações de perfil podem ser retornadas no id_token
, que é retornado após uma autenticação bem-sucedida. O aplicativo cliente do ASP.NET Core requer apenas o escopo do perfil. Ao usar o id_token
para declarações, nenhum mapeamento de declarações extra é necessário.
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;
});
Outra maneira de obter as declarações do usuário é usar a API de Informações do Usuário do OpenID Connect. O aplicativo cliente do ASP.NET Core usa a propriedade GetClaimsFromUserInfoEndpoint
para configurar isso. Uma diferença importante em relação às primeiras configurações é que você deve especificar as declarações necessárias por meio do método MapUniqueJsonKey
, caso contrário, somente as declarações padrão name
, given_name
e email
estarão disponíveis no aplicativo cliente. As declarações incluídas no id_token
são mapeadas por padrão. Essa é a principal diferença para a primeira opção. Você deve definir explicitamente algumas das declarações necessárias.
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");
});
Mapeamento de declaração de nome e declaração de função
A declaração Nome e a declaração Função são mapeadas para propriedades padrão no contexto HTTP ASP.NET Core. Às vezes, é necessário usar declarações diferentes para as propriedades padrão ou a declaração de nome e a declaração de função não correspondem aos valores padrão. As declarações podem ser mapeadas usando a propriedade TokenValidationParameters e definidas como qualquer declaração conforme necessário. Os valores das declarações podem ser usados diretamente na propriedade User.Identity.Name e nas funções.
Se o User.Identity.Name
não tiver nenhum valor ou as funções estiverem ausentes, verifique os valores nas declarações retornadas e defina os valores NameClaimType
e RoleClaimType
. As declarações retornadas da autenticação do cliente podem ser exibidas no 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"
};
});
Namespaces de declarações, namespaces padrão
O ASP.NET Core adiciona namespaces padrão a algumas declarações conhecidas, o que pode não ser necessário no aplicativo. Opcionalmente, desabilite esses namespaces adicionados e use as declarações exatas que o servidor OpenID Connect criou.
public void Configure(IApplicationBuilder app)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
Estender ou adicionar declarações personalizadas por meio de IClaimsTransformation
A interface IClaimsTransformation
pode ser usada para adicionar declarações extras à classe ClaimsPrincipal
. A interface exige um único método TransformAsync
. Esse método pode ser chamado várias vezes. Só adicione uma nova declaração se ela ainda não existir no ClaimsPrincipal
. Um ClaimsIdentity
é criado para adicionar as novas declarações e isso pode ser adicionado ao 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);
}
}
A interface IClaimsTransformation
e a classe MyClaimsTransformation
podem ser adicionadas no método ConfigureServices como um serviço.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IClaimsTransformation, MyClaimsTransformation>();
Como estender ou adicionar declarações personalizadas no ASP.NET Core Identity
Consulte a seguinte documentação:
Adicionar declarações a Identity por meio de IUserClaimsPrincipalFactory
Mapear declarações de provedores de identity externos
Consulte a seguinte documentação:
Para saber mais, consulte Persistir declarações e tokens adicionais de provedores externos no ASP.NET Core.