ASP.NET Core 2.0: JWT + Identity Core na autenticação de APIs

Renato Groffe
7 min readJan 28, 2018

O uso de JSON Web Tokens (JWT) constitui uma das práticas mais comuns para garantir um acesso seguro a APIs REST. Partindo de tokens criptografados com um tempo de validade pré-definido, esta técnica é suportada pelas principais plataformas de desenvolvimento da atualidade.

Já abordei inclusive em um artigo anterior a utilização de JWT como mecanismo de autenticação em APIs REST construídas com o ASP.NET Core 2.0. O post em questão fazia uso de um banco de dados contendo usuários e chaves de acesso, além de demonstrar os passos necessários para a geração de tokens e como proteger o acesso a recursos disponibilizados por uma API:

ASP.NET Core 2.0: autenticação em APIs utilizando JWT (JSON Web Tokens)

O banco de dados mencionado corresponde a um controle customizado para gerenciamento de usuários e credenciais de acesso. Outras opções similares podem ser empregadas e o ASP.NET Core Identity constitui um bom exemplo neste sentido.

Fazendo uso de bases relacionais e do Entity Framework Core como tecnologia de acesso a dados, o ASP.NET Core Identity conta com funcionalidades e recursos que simplificam a implementação de controles de segurança em aplicações Web construídas com o .NET Core.

Este novo artigo foca no uso de JWT em conjunto com o ASP.NET Identity Core, a fim de garantir o acesso seguro a APIs REST baseadas no ASP.NET Core 2.0.

Aproveito este post para deixar aqui também um convite. No dia 29/01 (segunda) às 22h00 — horário de Brasília — farei uma apresentação no Canal .NET sobre as novidades do C# 7.2, juntamente com o MVP André Secco e utilizando para isto o release mais recente do Visual Studio 2017 (Update 15.5).

Para efetuar a sua inscrição acesse a página do evento no Facebook ou então o Meetup. A transmissão acontecerá via YouTube, em um link a ser divulgado em breve.

Habilitando o uso do Identity em projetos ASP.NET: uma visão geral

Ao criar um novo projeto do tipo Web Application ou Web Application (Model-View-Controller) a partir do Visual Studio 2017 podemos definir como será a autenticação através da opção Change Authentication:

Selecionando então o modo Individual User Accounts e, na sequência, a opção Store user accounts in-app:

A confirmação destes procedimentos resultará na criação de tipos como ApplicationDbContext e ApplicationUser, além de outras classes, Controllers e Views empregados no gerenciamento de usuários e controle de acesso:

Já no caso de projetos Web API não contamos com a opção Store user accounts in-app ao empregar o modo Individual User Accounts:

Esta característica trará a necessidade de implementar manualmente em nossas APIs algumas estruturas e instruções para integração com o ASP.NET Core Identity. As próximas seções descrevem em detalhes os ajustes necessários para se atingir tal objetivo.

Alguns detalhes sobre a aplicação de exemplo

A API apresentada neste post já foi detalhada no primeiro artigo que publiquei sobre JWT, fornecendo uma funcionalidade para a conversão de alturas em pés (unidade comumente utilizada na aviação) para o equivalente em metros.

Os fontes do novo projeto que combina o uso de JWT com o ASP.NET Core Identity já se encontram no GitHub (esta aplicação foi criada empregando o ASP.NET Core 2.0):

https://github.com/renatogroffe/ASPNETCore2_JWT-Identity

Implementando a integração com o ASP.NET Core Identity

No arquivo appsettings.json será incluída a string de conexão para acesso ao banco BaseIdentity (que conterá as tabelas das quais o ASP.NET Core Identity depende), bem como configurações para a geração do token (Audience, Issuer e tempo de duração em segundos):

A classe ApplicationUser herda do tipo IdentityUser (namespace Microsoft.AspNetCore.Identity), correspondendo à implementação dos dados de um usuário registrado em uma base configurada para uso do ASP.NET Core Identity:

Na próxima listagem está a implementação das seguintes estruturas:

  • User: classe empregada na manipulação de credenciais de usuários;
  • Roles: tipo estático contendo o nome da role de acesso (Acesso-APIAlturas) à API de conversão de alturas;
  • TokenConfigurations: classe que conterá configurações (Audience, Issuer - emissor, Seconds - tempo de validade em segundos) empregadas na geração de tokens. Estas definições serão carregadas a partir do arquivo appsettings.json.

Já a classe de contexto ApplicationDbContext implementa o tipo genérico IdentityDbContext (namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore), referenciando também ApplicationUser em sua definição. Esta construção baseada no Entity Framework Core acessará o banco BaseIdentity, sendo empregada pelas demais estruturas do ASP.NET Identity Core para gerenciar dados de usuários e permissões:

A classe IdentityInitializer será responsável pela inicialização dos dados para utilização do ASP.NET Identity Core:

  • O construtor de IdentityInitializer receberá instâncias dos tipos ApplicationDbContext, UserManager (namespace Microsoft.AspNetCore.Identity) e RoleManager (namespace Microsoft.AspNetCore.Identity);
  • O método Initialize criará, caso ainda não existam, as estruturas de dados utilizadas pelo Identity (o que inclui o próprio banco de dados, indicado em appsettings.json pelo nome BaseIdentity). Isto acontecerá através de uma chamada ao método EnsureCreated do objeto Database vinculado a ApplicationDbContext;
  • Já a instância do tipo RoleManager será empregada na criação da role Acesso-APIAlturas (se a mesma ainda não existir);
  • As chamadas ao método CreateUser registrarão os usuários admin_apialturas (com permissão de acesso à API de conversão de alturas) e usrinvalido_apialturas (sem permissão), utilizando para tanto a instância da classe UserManager.

O tipo SigningConfigurations já foi detalhado no artigo anterior e não passará por mudanças. É responsabilidade desta estrutura a geração da chave e da assinatura empregadas na criação de tokens:

Ajustes serão realizados também na classe Startup, a fim de possibilitar o uso do ASP.NET Identity Core (as instruções necessárias para a utilização de tokens já foram descritas no primeiro artigo sobre JWT, o qual foi mencionado no início deste post):

  • No método ConfigureServices serão invocados os métodos AddDbContext (configurando o uso da classe ApplicationDbContext) e AddIdentity (habilitando a utilização das estruturas do Identity na API via injeção de dependências);
  • A chamada ao método Initialize de IdentityInitializer na operação Configure permitirá a criação de estruturas, usuários e permissões na base designada para uso do ASP.NET Identity Core (caso tais elementos ainda não existam). Em um cenário ideal esta instrução seria evitada, já que o gerenciamento de usuários aconteceria separadamente da API REST aqui descrita (em uma aplicação Web isolada que acessa um banco de dados pré-existente, por exemplo).

O tipo LoginController passará pelas seguintes alterações (novamente foram omitidas as explicações quanto à geração de tokens, descritas no artigo anterior sobre JWT):

  • Instâncias dos tipos UserManager e SignInManager (namespace Microsoft.AspNetCore.Identity) serão recebidas via injeção de dependências no método Post;
  • A referência de UserManager será empregada para validar a existência de um usuário (cujas credenciais foram informadas na instância do tipo User);
  • Já o objeto do tipo SignInManager validará a senha informada, utilizando para isto o método CheckPasswordSignInAsync.

A classe ConversorAlturasController permanecerá inalterada, tendo por função a conversão de alturas em pés para o equivalente em metros (o acesso à Action Get será permitido apenas se as requisições recebidas conterem um token válido):

Testes

Ao acionar a execução do projeto APIAlturas serão criadas as estruturas esperadas para o banco BaseIdentity (e eventualmente o próprio) caso tais construções ainda não existam. A consulta a seguir mostra que foram populadas as tabelas dbo.AspNetUsers, dbo.AspNetRoles e dbo.AspNetUserRoles:

Consultando o banco BaseIdentity via SQL Operations Studio

Testes de acesso à API serão realizados então via Postman. Uma primeira simulação será feita por meio da URL http://localhost:56435/api/login, informando no corpo desta solicitação do tipo POST uma string JSON contendo o usuário admin-apialturas (userID) e sua respectiva senha (password). No retorno produzido pela API constará o token de acesso:

Uma requisição GET para a conversão de uma altura de 100 pés será enviada agora, utilizando o token obtido no passo anterior. Esta solicitação terá como URL o valor http://localhost:56435/api/conversoralturas/pesmetros/100, sendo que o resultado da mesma pode ser observado na próxima imagem:

Um teste com o usuário usrinvalido_apialturas indicará que o mesmo não possui acesso à API de conversão de alturas:

Caso necessite consumir APIs REST que utilizam JWT em .NET Core acesse o seguinte post:

.NET Core 2.0 + JWT: consumindo uma API que utiliza tokens

E para concluir este artigo não deixe também de acompanhar o post a seguir, em que venho agrupando todos os conteúdos que tenho produzido sobre .NET Core 2.0 e ASP.NET Core 2.0:

Conteúdos gratuitos sobre .NET Core 2.0 e ASP.NET Core 2.0

--

--

Renato Groffe

Microsoft Most Valuable Professional (MVP), Multi-Plataform Technical Audience Contributor (MTAC), Software Engineer, Technical Writer and Speaker