Cell CMS — Autenticando o Admin
Uma vez, durante um projeto, levei o maior esporro de um amigo por jogar para sprints tardios a parte de autenticação. Ele dizia que:
“Fazer isso é jogar algo de muito valor para o usuário pro canto, vai gerar a maior dor de cabeça para amarrar todos os Models, Services e Rotinas. Isso deveria ter sido uma das primeiras coisas a serem entregues!”
Ele estava tremendamente certo! Eu, de fato, adiava a parte de autenticação pois era, na época, era receoso com fazer a implementação disso do zero. Lição aprendida arduamente, agora todo projeto já começo pensando em como irei autenticar e autorizar os usuários.
Antes de começarmos o post de hoje, deixo aqui o link para o código fonte. Não colocarei todos os refactors e ajustes nos posts, porém tentarei manter todos os commits atômicos, para que seja prático consultar exatamente o que foi feito entre cada feature/post!
Vamos começar preparando o nosso Tenant no Azure. Caso você não tenha uma conta no Azure crie-a (existem até promoções para novas contas, como créditos extras ou até mais funcionalidades gratuítas). O Recurso que vamos utilizar é gratuíto para o uso simples, apenas se quisermos algumas funcionalidade “empresariais” que teriamos de realizar um upgrade para versões pagas.
Criada a sua conta, acesse https://portal.azure.com e busque por “Azure Active Directory” na busca de recursos:
Dentro do blade do Azure AD procure por “App registrations”, entre nela e clique em “New registration”:
Escolha um nome para sua aplicação (no nosso caso “Cell CMS”), que tipo de contas podem utilizar sua aplicação e adicione a URL “https://localhost:5001/oauth2-redirect.html” como uma Web Redirect URI.
A URL https://localhost:5001/oauth2-redirect.html é o padrão do Swashbuckle.AspnetCore.SwaggerUi. Ela pode ser alterada, via código, porém costumamos utilizar ela em seu valor default
Uma breve explicação sobre os tipos de contas (maiores detalhes podem ser encontrados na documentação)
- Accounts in this organizational directory only: O nível mais restrito, apenas pessoas que já estiverem cadastradas em nosso Azure poderão fazer login no nosso app
- Accounts in any organizational directory: O nível intermediária, apenas pessoas com contas de trabalho ou educacionais poderão utilizar o App. Por exemplo uma pessoa com a conta aluno@universidade.onmicrosoft.com poderia fazer login no nosso app.
- Accounts in any organizational directory and personal Microsoft Accounts: O nível mais amplo, todos os tipos de contas da Microsoft (pessoais, empresariais, educacionais, xbox, skype, etc etc etc) poderão fazer login no nosso app.
Criado o app anote os seguintes valores que são exibidos na tela, os utilizaremos mais tarde:
- Application (client) id: É um ID que identifica nosso app, será utilizado para identificar “quem” está pedindo autenticação no Azure AD
- Directory (tenant) id: É um ID que identifica nosso tenant (ou seja, nosso Azure AD), poderemos utilizar este no futuro para diferenciar tenants.
Em seguida, clique em “Endpoints” e anote os seguintes endpoints:
- OAuth 2.0 Authorization endpoint (v2): É o endpoint que vai permitir que os usuários do Azure AD “autorizem” nosso app a ver quem eles são
- OAuth 2.0 token endpoints (v2): É o endpoint que vai emitir tokens, desde que autorizados pelo usuário, que identificam o usuário do Azure AD
- OpenID Connect metadata document: É um
json
que possui todas as configurações necessárias para clients (por exemplo nosso futuro frontend) saberem “conversar” com o Azure AD.
Agora precisamos habilitar o Implicit Flow para que o SwaggerUI e o nosso futuro frontend possam utilizar este fluxo de autenticação. Futuramente farei um post explicando cada fluxo em detalhes, por hora acesse a aba “Authentication” e habilite os dois checks para o Implicit Grant:
Finalmente, vamos à aba Expose an API, defina um applicationId para a sua API e crie um novo Scope, conforme:
A ideia aqui é criar um Scope que permitirá que apenas alguns usuários possam utilizar nosso SwaggerUi. Criado o Scope, navegue à aba “Api Permissions” e escolha a opção “Add a Permission”, aba “My APIs” e selecione “Cell CMS”:
Agora selecione o Scope que criamos anteriormente e clique em Add Permissions:
Vamos voltar à api! Para quem quiser acompanhar direto pelos commits é só procurar os commits relacionadas ao branch feature/azure-ad-auth
.
Nossa primeira preocupação vai ser trazer, para a API, as configurações! Lembram os campos que anotamos? Precisamos deles na API. Para isso, vamos criar uma interface IAzureAdSettings
que será implementada pela classe AzureAdSettings
:
A constante SettingsKey
está ali para facilitar nossa vida ao buscar estes dados na IConfiguration
do DotNet Core. Vamos editar nosso appsettings.json
para armazenar estes valores. Substitua apenas o valor para o ClientId
pelo criado em seu Azure Active Directory
Para definirmos o ClientId
de maneira mais segura, clique com o botão direito no projeto da API e escolha Manage User Settings, alterando o json
que ele abrir no Visual Studio para conter o AzureAd__ClientId
:
Finalizado nossos preparativos, vamos utilizar estas configurações! Vamos adicionar, já, um novo NuGet que precisaremos para lidar com JWT: Microsoft.AspNetCore.Authentication.JwtBearer
. Adicionado o NuGet, vamos voltar ao Startup.cs
e planejar nossas alterações:
- Precisamos buscar as Configurações do Azure AD através do nosso
IConfiguration
- Com estas configurações vamos adicionar e configurar os serviços de autenticação
- Vamos, finalmente, utilizar o Middleware de autenticação
Agora precisamos informar ao Swagger.json que nossa API suporta autenticação e configurar para que o SwaggerUI consiga realizar a autenticação em nosso nome. Para atingir nosso objetivo vamos:
- Criar um filtro para que o
Swashbuckle.Swagger
saiba quais endpoints requerem autenticação - Aplicar este filtro na geração do
Swagger.json
- Definir, na API, os Scopes que configuramos para a aplicação
- Adicionar à geração do
Swagger.json
informações sobre nossos endpoints de autenticação - Adicionar ao SwaggerUi o ClientId
O código abaixo já está considerando um refactor que realizei no branch feature/refactor-swagger-config
, que move a configuração do Swagger para Extension Methods, deixando nosso Startup.cs
mais limpo:
Finalmente, para testarmos toda essa configuração, vamos ao WeatherForecastController
adicionar o attribute [Authorize]
ao método Get()
. Feito isso, basta executar sua API, você será recepcionado pelo seguinte SwaggerUi:
Conseguirá fazer login através do Authorize:
E, finalmente, conseguirá chamar o endpoint protegido:
Finalizaremos por aqui este post! No próximo post partiremos para a implementação inicial da Pesistência, utilizando EntityFrameworkCore e Sqlite! Caso queria ver o resultado final no GitHub basta acessar este merge commit. Obrigado por ler meu post e abraços!