Autenticação e Autorização de uma ASP .NET Web API com Keycloak
Tags: beginners, programming, api, security
Published: 2023-08-22 21:52:37

Olá pessoal!

No meu primeiro artigo eu gostaria de falar um pouco sobre autenticação, autorização e como podemos realiza-los de uma maneira muito prática e performática utilizando Keycloak.

## O que é autorização e autenticação?
Esses dois conceitos são utilizados frequentemente quando estamos falando de segurança de aplicações, mas se referem a aspectos distintos no gerenciamento de acesso a recursos.
- **Autenticação**: É a verificação da identidade do usuário. O primeiro processo quando você tenta acessar alguma aplicação, e nesse processo, é verificado se você é realmente quem você diz quem é e se a aplicação o conhece. Usualmente é feito por algum tipo de "segredo", como uma senha, uma assinatura digital ou uma impressão digital.
- **Autorização** : Após verificado a identidade do usuário com sucesso, e o usuário já se encontra dentro da aplicação, é necessário limitar quais recursos aquele usuário pode acessar ou não pode acessar. Aqui entra a autorização: é a definição de quais açoes, operaçoes ou recursos um usuário ou um grupo tem acesso.

## Introdução ao Keycloak
De maneira sucinta, o Keycloak é um gerenciador de identidade e acesso open source, que adiciona autenticação a suas aplicações e evita a necessidade de armazenar ou autenticar usuários.
Entre suas principais funcionalidades, é interessante ser destacado que o keycloak: 
- Realiza SSO nas suas aplicações (Single-Sign-on)
- Gerencia seus usuários
- Gerencia funcionalidades por meio de UI interativa
- Realiza isolamento de Tenants ( no Keycloak são os _realms_ que achei super legal )
- Reliza federação de usuários
- Conta com provedores externos de identidade (como GitHub, Instagram, Facebook etc)
- Possui diversos serviços de autenticação como OpenID, oAuth 2.0 e SAML 2.0.

## Demonstração 

Para demonstrar a facilidade de realizar a autenticação e autorização em uma aplicação, irei demonstrar passo a passo como podemos:
1) [Iniciar o KeyCloak via Docker;](#iniciando-o-keycloak-via-docker)
2) [Criar um novo realm;](#criação-de-um-novo-realm)
3) [Criar usuários;](#criação-de-usuários)
4) [Criar uma web-api e configurar o Keycloak;](#criação-da-web-api-e-configuração-do-keycloak)
5) [Testar endpoints protegidas](#testando-os-endpoints-protegidos)
6) [Criar grupos, roles e associar grupos, usuários e roles;](#criação-de-grupos-e-roles-e-associação-de-usuários-e-grupos)
7) [Testar novamente os endpoints;](#testar-novamente-os-endpoints)

- Requisitos:
	- Docker
	- .NET 6.0
	- Postman ou Insomnia

## Iniciando o Keycloak via Docker
- Para esse passo, é necessário que você tenha o Docker instalado. Se não tiver, é só seguir o passo a passo na própria documentação (https://www.docker.com/get-started/)
- Abra um terminal e digite o seguinte comando:
```bash
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:22.0.1 start-dev
```
> Aqui, estamos expondo a porta 8080 e configurando as chaves de ambiente para acesso ao Console de Administrador e iniciando o Keycloak em modo de desenvolvimento com o start-dev.

Vamos para o console administrador via http://localhost:8080/admin/master/console/ e podemos acessar pelas credenciais cadastradas acima.

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cdv0wscmhpwumo8qqne9.png)

## Criação de um novo Realm
Vamos realizar a criação de um novo realm para nossa aplicação
Clique no dropdown embaixo de KeyCloak e clique em Create Realm

![createrealm](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mcmxoqoko9emnx852mue.png)
Dê um nome para o seu realm, no caso estarei nomeando o meu como **mordor**
 
![mordorrealm](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n9ebyhlv2seq5fr7pttz.png)
Feito isso, o realm já estará ativo e selecionado e podemos passar para o próximo passo.

## Criação de usuários.
Dentro do realm que acabamos de criar, podemos realizar a criação de usuários clicando em Users no painel lateral e depois em Add User


![users](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sv9suxqfr0xphmjx8slr.png)

Irei criar inicialmente três usuários: Sam, Frodo e Sauron. Todos eles com apenas username e name.
Ao criar um usuário, precisamos definir a sua senha. Clique em um usuário e na aba Credentials, clique em Set Password e desmarque senha temporária apenas para testes.


![pass](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jm54ur423zsslx0z1qb9.png)

Feito isso, já temos nossos tres usuários criados e podemos partir para a criação de nossa web-api para realizarmos nossos primeiros testes de autenticação! 

## Criação da web-api e configuração do Keycloak 
Crie uma nova aplicação do tipo web-api. Pode ser feito via UI pelo Visual Studio ou pelo comando:
```bash
dotnet new webapi -n NomeDoProjeto
```
Instale as bibliotecas do KeyCloak necessárias via NuGet
```bash
dotnet add package Keycloak.AuthServices.Authorization
dotnet add package Keycloak.AuthServices.Authentication
```
Para verificar se foi instalado corretamente, podemos abrir o `csproj` do nosso projeto. Deverá estar assim: 


![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxg7t6hlnnytst3lphv0.png)

Precisamos fazer com que o KeyCloak identifique nossa aplicação, isso significa que precisaremos criar um **Client** pelo Console de Administrador.

No painel lateral, clique em Client e depois em Create Client. O meu Client ID será *mordor-api* e clique em Next.

 
![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqjqat9dez9h8tl8htsa.png)

Iremos marcar Client authentication e Authorization e clicar em Next

  
![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/21ztfyj50gasjgewd75h.png)

No Login Settings, podemos definir qual a Home URL e as URL de redirecionamento, ou URL após realizar o Logout. Como estamos fazendo uma demonstração simples de uma web-api, não iremos preencher nenhum desses campos, mas é interessante notar que esses campos precisam ser preenchidos quando temos uma tela de login, e precisamos realizar o redirecionamento para nossa home page após a autenticação de um usuário.

Criado nosso primeiro Client, iremos definir agora o **Client Scope**, que se refere a um conjunto de permissões, atributos ou funcionalidades que são concedidos a um aplicativo durante a autenticação e autorização. No painel lateral, vamos clicar em Client scopes e depois em Create client scope.

  
![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k7l0en0w51lca1fb1tsw.png)

Dê um nome ao seu escopo, selecione o Type como Default e o prococolo sendo OpenID Connect.

  
![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t99o3oo6p7lh96kj5xgd.png)

Após criado o escopo, iremos definir alguns **Mappers**, que serão certos campos que serão mapeados dentro do token.

  
![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pvvefncfx5h6i2mioq9n.png)

Primeiro, iremos adicionar um mapper pre-definido para adicionar as roles de um client dentro do token.

   
![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/584xt7kyparvxhw98n1s.png)

Iremos adicionar um novo mapper configurado, para adicionar o campo de Audience no token. Clique em AddMapper e depois em ByConfiguration e depois em Audience.


![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xz9iphy77fzvyyrl9wx.png)

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ptbo47pv94jb07cmkhpw.png)

Nosso escopo deverá ter dois mappers:

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5o0h81hqjnfh2xjk11qk.png)

Precisamos adicionar o scope criado ao nosso client. Na aba Clients, clique no client que você criou e depois disso na aba superior Client scopes e Add client scope.

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h7vrzqv9u4rzhta7hpap.png)

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rm3t8ewt602l0iinhe4c.png)
Iremos extrair as configurações necessárias do nosso Client, para configurar nossa web-api. Ainda no menu Clients, clique no dropdown Action no canto superior direito e depois em Download Adapter config. 


![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pmes0y2ojmegl327i6vd.png)

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dtd1djv1x9uqghlxq9ex.png)
Altere o campo "ssl-required": "external" para "ssl-required": "none", visto que não iremos utilizar nenhum tipo de certificado.
Copie todo esse JSON, e no `appsettings.Development.json`  coloque a chave como sendo "Keycloak". Suas configurações deverão ficar assim:

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c61hcias9i25ap9g7znl.png)

Agora, precisamos adicionar a autenticação e autorização no nosso `Program.cs`.
Adicione as seguintes linhas de código ao seu `Program.cs`:
```csharp
var authenticationOptions = builder
                            .Configuration
                            .GetSection(KeycloakAuthenticationOptions.Section)
                            .Get<KeycloakAuthenticationOptions>();

builder.Services.AddKeycloakAuthentication(authenticationOptions);

var authorizationOptions = builder
                            .Configuration
                            .GetSection(KeycloakProtectionClientOptions.Section)
                            .Get<KeycloakProtectionClientOptions>();

builder.Services.AddKeycloakAuthorization(authorizationOptions);
```
É importante notar que a Section no `appsettings.json` deverá ser exatamente `Keycloak`, visto que o `KeycloakAuthenticationOptions.Section` é o padrão da biblioteca. Dessa maneira, o KeycloakAuthenticationOptions será bindado corretamente com as informações obtidas pelo adapter, que extraimos do console de administrador.
Ah, não se esqueça também de adicionar:
```csharp
app.UseAuthentication();

app.UseAuthorization();
```
Feito isso, crie um Controller e alguns endpoints para efetuar testes. Criei dois endpoints assim:
```csharp
[HttpGet]
[Authorize]
public ActionResult CreateRing()
{
    return Ok("Ash nazg durbatulûk, ash nazg gimbatul, ash nazg thrakatulûk, agh burzum-ishi krimpatul");
}

[HttpDelete]
[Authorize]
public ActionResult DestroyRing()
{
	return NoContent();
}
```

## Testando os endpoints protegidos
Com o Postman, podemos obter o `acess_token` enviando um request do tipo `POST` para http://localhost:8080/realms/mordor/protocol/openid-connect/token.
No `Body` da requisição, iremos realizar o grant_type do acess_token por meio da senha configurada para o usuário

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rh09brzddtohjuz9qbh9.png)
Se tudo estiver correto, como resposta iremos receber uma resposta assim: 


![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k7mcuor2lii0j3eyl3f8.png)

Podemos colar o `access_token` no site https://jwt.io/ para verificar quais foram os campos que vieram no nosso JWT! 

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czv54wm0d16endnp1bch.png)

Iremos testar um dos nossos endpoints protegidos sem estarmos autorizados:

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0wne8igswdbb7d0hpi8s.png)
Podemos observar que o Status foi 401 Unauthorized. Agora, nos autorizarmos colando o `access_token` no Bearer Token obtemos um Status 200.

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubix9qzan56d9hbxpo1b.png)

## Criação de grupos e roles e associação de usuários e grupos.
Voltando ao Console de Administrador do KeyCloak, podemos realizar a criação de grupos no painel lateral. Irei criar apenas um grupo chamado FellowshipOfTheRing

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppvzh33ki0c2ezvqs5ln.png)
Agora iremos criar uma role para o nosso Client. Na aba Client, selecione o client que criamos para realizar a demonstração e na aba Roles, crie uma nova Role. Criei duas roles como indicado abaixo.

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlqpuxqi846ffjv7bg7t.png)
Vamos colocar Frodo e Sam (os usuários que criei) dentro do grupo FellowshipOfTheRing. Clique na aba groups e depois clique em Members e AddMember

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/llvirs7xkl2dlzw5qwaf.png)
Mapearemos apenas uma role para nosso grupo. Clique na aba Role mapping e depois em Assign Role. Filtramos por Clients (isso significa que podemos ver as roles que criamos no nosso client) e atribuimos a role DestroyRing para nosso grupo.

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h376ua4rv0m8ulrpdef7.png)
Agora, no painel lateral, em Users, iremos atribuir a role CreateRing para o usuario Sauron.

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dlnu5fqya2pbmwavfawo.png)

## Testar novamente os endpoints.
Feitas as novas configuraçoes de acesso aos nossos recursos, podemos configurar nossas endpoints de acordo com essas configuraçoes de Roles. No Controller, irei mudar as rotas inserindo o parametro que irá verificar as Roles que o usuário possui.
```csharp
[HttpGet]
[Authorize(Roles ="CreateRing")]
public ActionResult CreateRing()
{
    return Ok("Ash nazg durbatulûk, ash nazg gimbatul, ash nazg thrakatulûk, agh burzum-ishi krimpatul");
}

[HttpDelete]
[Authorize(Roles ="DestroyRing")]
public ActionResult DestroyRing()
{
	return NoContent();
}
```
No Postman, vamos receber o `access_token` como Sauron, e tentar acessar o endpoint DestroyRing.

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tj70lw6yngeur3j45rrr.png)

Podemos perceber que o Status Code foi 403 Forbidden, visto que Sauron não possui a Role `DestroyRing` atribuida a ele. Agora se formos acessar a rota CreateRing obtemos o StatusCode 200, visto que o usuário Sauron possui a role necessária para o acesso ao endpoint. 

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i7j6m7q51x7o7f84q5x8.png)
Se solicitarmos o access_token como Frodo ou como Sam, será que podemos acessar a endpoint CreateRing visto que atribuimos a role apenas para o grupo? A resposta é sim! Uma role atribuida a um grupo é repassada para os usuários que estão atribuidos a este grupo!
Obtendo o `access_token` e verificando o token no https://jwt.io/ verificamos a Role que atribuimos ao grupo FellowshipOfTheRing e podemos realizar normalmente a requisição para o endpoint.

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sif4x02y0qw3ppjcvxay.png)

![img](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1edfdjugtf3koe8pieb8.png)

## Conclusão

Podemos perceber que as funcionalidades do Keycloak são inúmeras, e facilitam a nossa vida como desenvolvedor. As documentações são bastante claras e apresentam diversas explicações de como podemos implementar as features que o Keycloak oferecem em nossos projetos e em empresas. 

Espero que tenham gostado desse passo-a-passo! Pretendo aprofundar um pouco mais no tópico e irei trazer mais novidades por aqui! 

Segue o repositório da demonstração 
{% embed https://github.com/gloinho/mordor-api %}

Até a proxima!