Cutomizando Claims no ASP.NET
29/08/2023
André Baltieri

Neste artigo vamos aprender a armazenar Claims customizados e fazer a leitura deles posteriormente no ASP.NET.

O que são Claims?

A tradução da palavra Claim é afirmação e podemos dizer que no .NET, um Claim nada mais é do que uma composição de chave/valor que afirma algo.

Dada esta composição, podemos tomar ações de autorização em nossa aplicações ou APIs, da forma que desejarmos.

Claims padrões

O .NET também traz alguns Claims padrões, dentro do enumerador ClaimTypes que nos fornece dentre outros o [ClaimTypes.Name](http://ClaimTypes.Name) que serve como base para obter o nome do usuário.

new Claim(ClaimTypes.Name, "xyz@balta.io")

ClaimTypes.Name e ClaimTypes.Role

Existem dois Claims em específico que são extremamente úteis no ciclo de vida de qualquer aplicação ASP.NET ou Blazor, e eles são o ClaimTypes.Name e ClaimTypes.Role.

Sempre que atribuímos o ClaimTypes.Name temos acesso instantâneo a ele através do User.Identity.Name.

...
new Claim(ClaimTypes.Name, "andré");
...

public IActionResult GetAsync()
{
	var name = User.Identity.Name; // andré
}

O mesmo vale para o ClaimTypes.Role que podemos atribuir várias vezes e posteriormente verificar através do User.IsInRole ou pelo atributo Authorize.

...
new Claim(ClaimTypes.Role, "student");
new Claim(ClaimTypes.Role, "premium");
...

[Authorize(Roles = "student")]
public IActionResult GetAsync()
{
	if(User.IsInRole("premium"))
		...
}

Como criar Claims Customizados

Mas você sabia que podemos criar Claims customizados? Que não estão definidos no enumerador ClaimTypes?

Dado que um Claim é apenas uma composição de Chave/Valor, podemos utilizar qualquer literal para sua chave. Na verdade o ClaimTypes é apenas um atalho para isto, que previne o uso de uma literal inválida.

Se analisarmos o código que gera os Claims mostrado no artigo anterior, teremos o seguinte:

private static ClaimsIdentity GenerateClaims(User user)
{
    var ci = new ClaimsIdentity();
    ci.AddClaim(new Claim(ClaimTypes.Name, user.Email));
    foreach (var role in user.Roles)
        ci.AddClaim(new Claim(ClaimTypes.Role, role));

    return ci;
}

Podemos modificar este código para adicionar qualquer Claim customizado, como desejarmos, no caso, para ser bem explícito, vamos utilizar o Claim abaixo.

private static ClaimsIdentity GenerateClaims(User user)
{
    var ci = new ClaimsIdentity();
    ci.AddClaim(new Claim(ClaimTypes.Name, user.Email));
    foreach (var role in user.Roles)
        ci.AddClaim(new Claim(ClaimTypes.Role, role));

		ci.AddClaim("fruit", "banana"); // 👈

    return ci;
}

IMPORTANTE Este Claim também será adicionado ao PayLoad do Token caso esteja trabalhando com APIs ou nos Cookies caso esteja trabalhando com ASP.NET. Então, nada de informação sensível aqui.

Como ler Claims Customizados

Com o Claim adicionado, agora precisamos obter esta informação, e isto pode ser feito através do objeto User.Claims, que contém todos os Claims do contexto atual.

User.Claims.FirstOrDefault(x => x.Type == "fruit"); // banana

Pronto, agora você tem tanto a criação como leitura de Claims customizados e pode utilizá-los para armazenar mais informações além do nome do usuário.

Extension Methods

Aproveitando a oportunidade, este é um ótimo cenário para se criar um Extension Method, que retorna os Claims que você precisa.

Assim você evita possíveis erros, já que o Claim pode não existir ou o valor dele, por ser uma string, pode não ser convertido para o tipo esperado.

Abaixo está um exemplo que utilizo, onde tenho extensões para ler o Id, Nome e E-mail do usuário, três Claims que sempre utilizo nas minhas aplicações.

public static class ClaimsPrincipalExtension
{
    public static Guid Id(this ClaimsPrincipal user)
    {
        try
        {
            var id = new Guid(user.Claims.FirstOrDefault(c => c.Type == "Id")?.Value ?? string.Empty);
            return id;
        }
        catch
        {
            return Guid.Empty;
        }
    }

    public static string Name(this ClaimsPrincipal user)
    {
        try
        {
            return user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value ?? string.Empty;
        }
        catch
        {
            return string.Empty;
        }
    }

    public static string Email(this ClaimsPrincipal user)
    {
        try
        {
            return user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value ?? string.Empty;
        }
        catch
        {
            return string.Empty;
        }
    }
}

Para utilizar basta importar o namespace onde criou sua extensão, no caso eu já faço isto utilizando o Global Using para não haver a necessidade de importar este namespace em todos os arquivos que precisarei dele.

global using Balta.Ui.Web.Extensions;

E assim utilizar a extensão, que funciona tanto para APIs, MVC, Minimal APIs e até Blazor.

// Razor
@User.Id()
@User.Name()
@User.Email()

// MVC
User.Id()
User.Name()
User.Email()

// Minimal APIs
user.Id()
user.Name()
user.Email()

Conclusão

Trabalhar com Claims customizados no ASP.NET e Blazor é uma tarefa relativamente simples e com ajuda do C# tornamos nossa vida ainda mais fácil.

Conheça o autor

André Baltieri

Microsoft MVP

Me dedico ao desenvolvimento de software desde 2003, sendo minha maior especialidade o Desenvolvimento Web. Durante esta jornada pude trabalhar presencialmente aqui no Brasil e Estados Unidos, atender remotamente times da ?ndia, Inglaterra e Holanda, receber 8x Microsoft MVP e realizar diversas consultorias em empresas e projetos de todos os tamanhos.

An unhandled error has occurred. Reload 🗙