ASP.NET Core - Autenticação e Autorização

Publicado em 6/27/2019 12:00:00 AM


Um termo recorrente nas APIs é a autenticação e autorização, os Roles, Claims, Policies, JWT, oAuth e tudo mais. Embora pareça um assunto complexo, não é, e vamos desmistificar tudo neste guia!

Autenticação

A autenticação diz quem você é. Ela é responsável por identificar o usuário ou quem estiver acessando a API.

É normal hoje em dia vermos autenticação com terceiros, como Facebook, Google e agora até a Apple.

Este mecanismo, interno ou externo, garante que quem está acessando sua API é realmente quem ele diz ser.

Autorização

A autorização por sua vez, diz o que o usuário pode fazer. Ao contrário da autenticação, dificilmente você poderá terceirizar sua autorização, visto que ela faz parte do seu negócio e muitas vezes é específica para uma aplicação.

Roles, Claims e Policies

A autorização se dá por meio de Roles, Claims e Policies, que restringem ou permitem o acesso do usuário a determinada parte da aplicação.

Um Role (Perfil) é um papel, uma função, um cargo, algo que define acesso a um ou um grupo de usuários em partes da aplicação.

Normalmente trabalhamos com Roles em cenários mais simples, onde não necessitamos de muita interação, onde por exemplo, apenas sabendo que o usuário possui o papel de Gerente, já podemos tomar alguma decisão.

Um Claim (Afirmação) é algo mais imperativo em que na maioria dos casos utilizamos pra realizar alguma interação antes de deixar o usuário prosseguir.

Por exemplo, se precisamos validar se o usuário pertence a uma empresa específica, se ele realizou determinada venda ou qualquer outra validação que dependa de banco de dados por exemplo, este é um cenário interessante para os Claims.

As Policies (Políticas) nada mais são que as aplicações de Roles e Claims para determinar contratos de acessos a determinadas áreas de uma aplicação.

Você pode ter vários Roles e Claims compondo uma política que determina acesso a uma parte do seu sistema.

oAuth

Open Authentication nada mais é do que um padrão que especifica como fazemos as autenticações. Com a chegada de APIs abertas há um tempo atrás, foram necessárias algumas especificações mínimas de segurança e o oAuth foi criado.

Hoje ele se consolidou no mercado sendo utilizado por gigantes como Facebook, Twitter, Instagram, Spotify, Netflix dentre outros.

Token

Um token é uma chave encriptada, contendo informações do usuário, que só pode ser desencriptada com outra chave (privada) que fica no servidor.

Pode parecer estranho dizer que temos informações do usuário (Não sensíveis) em uma chave, mas ela garante que não precisamos armazenar estes tokens, tornando possível o que chamamos de Stateless Applications, ou aplicações sem estado.

Isto é fundamental para o escalonamento de qualquer aplicação. Afinal, sempre que uma máquina nova da sua aplicação sobe, sua memória não é copiada.

JWT

É a sigla para Json Web Token e pronuncia-se JOT. O JWT nada mais é que o armazenamento das informações do token no formato JSON.

Antes de prosseguir...

O processo de autenticação inicia-se em uma aplicação (Normalmente frontend) fazendo uma requisição para sua API.

Nesta requisição serão enviados o usuário e senha. Neste momento, nem preciso dizer que suas aplicações PRECISAM trafegar sobre HTTPS, isto é uma exigência do Google para indexar seu site.

Com o usuário e senha, é feito o processo normal de verificação do login, e caso tudo ocorra bem, sua API utiliza uma chave PRIVADA, que só ela tem para gerar um TOKEN contendo as informações que você desejar, no formato JWT.

Neste momento, você tem uma chave enorme, com todas as informações que colocou previamente (Nunca armazene informações sensíveis nos tokens), encriptada com uma chave privada que só a API e no formato JSON.

Abaixo um exemplo de chave:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImFuZHJlYmFsdGllcmkiLCJTdG9yZSI6InVzZXIiLCJuYmYiOjE1NjE2MDI2NjYsImV4cCI6MTU2MjIwNzQ2NiwiaWF0IjoxNTYxNjAyNjY2fQ.y2cvZ02PLJG-BFPUAWn1fChTvS4rPOv_iEEsLPd7eBg

Essa chave decodificada tem o seguinte conteúdo

{'alg':'HS256','typ':'JWT'}{'unique_name':'andrebaltieri','Store':'user','nbf':1561602666,'exp':1562207466,'iat':1561602666}

No caso, além do nome de usuário e perfil, também informo a data/hora de criação e expiração da mesma, mas isto é uma particularidade de código.

Conforme comentei anteriormente, nós não ficamos “logados” na API, então após o Login, este token é retornado e deve ser enviado novamente a cada requisição.

Configurando sua API

A primeira coisa que precisamos fazer em nossa API é definir uma chave, um segredo, algo que só a API terá acesso.

Esta chave será utilizada para encriptar o token e afins posteriormente. Não vou focar em COMO, mas sim em O QUE você precisa fazer, e neste cenário, eu criei uma classe estática, chamada Settings.cs com minha chave.

Poderia utilizar o AppSettings dentre várias outras coisas, mas o cenário mais simples é este, e como nossa chave não tende a mudar, isto resolve o problema.

Importante: Se você mudar a chave, todos os tokens ainda ativos perderão a validade, já que será impossível desencriptá-los com uma chave diferente da original.

Com nossa chave definida, precisamos gerar nossa chave, e este processo pode ser feito de diferentes maneiras também, com RSA e afins, mas um dos mais simples e que funciona é este abaixo:

Adicionando Autenticação

O primeiro passo é adicionar autenticação a nossa aplicação, e para isto vamos utilizar o método AddAuthentication do ASP.NET Core, que traz uma implementação padrão ótima para o que precisamos.

Note que definimos o AuthenticationScheme como JWT, o qual desejamos utilizar aqui. Além disso, há um DefaultChallenge que definimos também, mas ele não nos interessa no momento.

Depois de definido nosso esquema, precisamos dizer como nossos tokens vão funcionar, quais configurações irão ter, e isto é feito no método AddJwtBearer, conforme abaixo.

O que mais importa aqui são os TokenValidationParameters, que neste caso, informamos nossa chave, chamando a função SymmetricSecurityKey do .NET para gerar a chave segura.

Precisamos também, informar ao ASP.NET Core para utilizar a autenticação, e isto é feito no método Configure.

Por fim, é importante sempre utilizar o SetCompatibilityVersion para garantir que nada quebrará em versões futuras. O resultado final da classe Startup.cs é este abaixo, mas calma que ele ainda irá mudar.

Adicionando Autorização

Se utilizamos o AddAuthentication para autenticação, o AddAuthorization vai para autorização. Intuitivo né.

Neste cenário teremos dois Claims, um para usuário e outro para administrador, e estes são configurados neste formato, também no método ConfigureServices da classe Startup.cs.

No código acima eu crio duas Policies e dois Claims, uma para cada cenário. Em adicional, sempre definimos uma “chave” para eles, no caso utilizei o nome da aplicação, “Store”.

Deste modo definimos as políticas e afirmações, mas ainda não estamos utilizando elas, veremos isto nos Controllers logo mais.

Políticas Globais

Particularmente, em diversos cenários acho mais fácil restringir TODA a API e liberar só o que preciso, conforme a demanda.

Desta forma, podemos definir políticas globais, dizendo que TODO endpoint da nossa API requer autenticação.

Isto é feito também no ConfigureServices, no método AddMvc, desta forma.

O resultado final da nossa classe Startup.cs pode ser conferido abaixo.

Gerando o Token

Para este exemplo, criei um serviço para geração do Token, para não ficar tudo no Controller, mas sinta-se à vontade em fazer de forma diferente.

Na verdade, tudo que precisamos fazer aqui é buscar um usuário do banco de dados, gerar um token e aplicar as políticas ao usuário. Não vou cobrir como buscar o usuário do banco, OK, não é o foco deste artigo!

O ASP.NET Core possui um JwtSecurityTokenHandler, que vai fazer todo o trabalho duro para nós, precisamos apenas passar para ele um TokenDescriptor, que nada mais é do que a definição do token a ser gerado, com os Claims a data de expiração e as credenciais de acesso.

Neste cenário, para não criar muitas classes também, adicionei uma propriedade Token no User, apenas para poder retornar o User direto para tela, sem precisar converter ele para uma ViewModel. Não faça isto em casa!

No TokenDescriptor, temos o Subject, que define o Claim principal, e seus Roles. Neste caso também deixei apenas um Role no usuário para facilitar. Se tiver mais de um Role, faz um ForEach e tá tudo certo.

Movendo adiante temos o Expires que é o tempo de vida do Token, como é uma API de exemplo e não quero ficar fazendo Refresh do Token, utilizei 7 dias, mas normalmente utilizo duas horas.

Por fim temos as credenciais de acesso, novamente utilizando nossa chave. O resultado final do nosso UserService é este.

Permitindo Acesso Anônimo

Quando adicionamos as políticas globais, nós restringimos todo acesso à nossa API, porém pelo menos o Login precisa ser liberado, e para isto, utilizamos o decorador AllowAnonymous nas ações dos nossos controladores.

Então, sempre que quiser liberar acesso a uma determinada ação do seu controlador, basta utilizar este decorador.

O resultado final do nosso AccountController fica desta forma.

Restringindo Acesso por Perfil

Toda nossa API está restrita, porém a restrição é apenas para usuários anônimos, ou seja, se o usuário estiver autenticado, ele já tem acesso.

Nós criamos duas políticas de acesso para a API, uma para usuário (user) e outra para administrador (admin), definidas lá no Startup.cs.

Para utilizar estas políticas e restringir o acesso por perfil, basta utilizar o decorador Authorize, informando o perfil que deve ter acesso. É importante lembrar que as Strings definidas são Case Sensitive, então cuidado com isto.

Pronto! Temos uma API com autenticação e autorização com Claims e Policies de uma forma simples e fácil!



Compartilhe este artigo

COMECE AGORA MESMO

André Baltieri

7x Microsoft MVP

Olá eu sou o André Baltieri, desenvolvedor Web desde 2003, já trabalhei no Brasil e nos EUA, em projetos de diversos tamanhos.

Recebi por sete vezes consecutivas, o prêmio de Most Valuable Professional da Microsoft, um reconhecimento global dado para os maiores influentes em suas tecnologias.

Atuei também como Head of People, recrutando, selecionando e direcionando a carreira de mais de 70 desenvolvedores em uma das melhores empresas para se trabalhar do Brasil.

Sou palestrante em diversos eventos como ASP.NET Conference, DevXperience, TDC, GDG dentre outros e desde 2012 realizo treinamentos e consultorias em diversas empresas do Brasil, acumulando uma ótima experiência que também transformo em cursos InCompany e Online.