C# - Criando API Login com Jwt Token - Autorização e Autenticação - Módulo 2
Tags: csharp, dotnet, programming
Published: 2022-03-02 17:04:54

Sejam bem vindos novamente marujos, bora continuar remando rumo à mais um módulo desse projeto incrível.

&nbsp;
 Código Módulo 2 -> [Repositório GitHub](https://github.com/mgpaixao/JWTAuthAuthenticationProject/tree/master/Modulo%202)

&nbsp;

![Remar](https://media.giphy.com/media/DPUEnpnceULzE7Wki5/giphy.gif)


&nbsp;
Nesse módulo irei abordar a evolução do projeto da API de Login com autorização através do JWT Token. [Modulo 1](https://dev.to/mgpaixao/criando-api-com-jwt-autorizacao-e-autenticacao-modulo-1-20b8)

- [Migração de .net 5 para .net 6](#migrando-net-5-para-net-6)
- [Crud de Roles](#crud-de-roles)
- [O Sistema sempre tem 1 admin](#o-sistema-sempre-tem-1-admin)


&nbsp;
# Migrando .Net 5 para .Net 6
&nbsp;


#### 1 - No arquivo .csproj altere o `TargertFramework` para `net6.0` e dentro do mesmo `<PropertyGroup>` adicione `<Nullable>enable</Nullable>` e `<ImplicitUsings>enable</ImplicitUsings>` e o resultado deve ficar assim:

```
  <PropertyGroup>	  
      <TargetFramework>net6.0</TargetFramework>
	  <Nullable>enable</Nullable>
	  <ImplicitUsings>enable</ImplicitUsings>	  
  </PropertyGroup>
```

#### 2 - Atualize os pacotes Nugets
#### 3 - Migrando dados da Startup.cs para Program.cs



No .net 6 você não precisa mais dos métodos `ConfigureServices()` e do `Configure()`, você precisa apenas estanciar  o `WebApplication Builder` na classe `program` para poder ter acesso aos métodos de configuração.
&nbsp;
Então no nosso programa o método `ConfigureServices()` ficou assim:
&nbsp;
**IMPORTANTE: Mudei o JWTkey para o nosso secrets, para não deixar exposto a nossa chave de criptografia.**
&nbsp;
```
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<TokenService>();
builder.Services.AddDbContext<DataContext>();
builder.Services.AddControllers();


//JWTConfig 
var key = Encoding.ASCII.GetBytes(builder.Configuration["Key:JwtKey"].ToString());
builder.Services.AddAuthentication(x =>
{
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
    x.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        ValidateAudience = false,
        IssuerSigningKey = new SymmetricSecurityKey(key),
        ValidateIssuer = false
    };
});

//Configuração do Swagger para adicionar o Bearer Token na auth
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWTAuthAuthentication2", Version = "v1" });
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
    {
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer",
        BearerFormat = "JWT",
        In = ParameterLocation.Header,
        Description = "JWT Authorization header using the Bearer scheme",
    });
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                          new OpenApiSecurityScheme
                          {
                              Reference = new OpenApiReference
                              {
                                  Type = ReferenceType.SecurityScheme,
                                  Id = "Bearer"
                              }
                          },
                         new string[] {}
                    }
                });
});
```

E o método `Configure()` vai ficar assim:


```
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWTAuthAuthentication2 v1"));
}
app.UseHttpsRedirection();
app.UseRouting();

////JWTConfig - Adicionar sempre Authentication antes de Authorization
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});
app.Run();
```

Repare que não fiz grandes alterações, só passei as configurações da startup para a classe `Program.cs` e agora você pode excluir a classe `Startup.cs` adotoando os padrões do .net 6



# Crud de Roles
&nbsp;
### Create Read Update Delete - CRUD
&nbsp;
![Crud](https://media.giphy.com/media/YO3icZKE2G8OoGHWC9/giphy.gif)
Como vamos fazer um crud completo da entidade Roles, é de boa prática criar uma Controller só pra isso, separando as responsabilidades.

Nessa Controller só terá autorização quem tiver acesso de Admin.
Regas de negócio que aplicamos:

- 1 - Usuário não pode alterar sua própria role
- 2 - Precisa ter ao menos 1 usuário admin no banco de dados
- 3 - Não pode haver duas Roles com o mesmo nome

E assim ficou a `RolesController`

## Post
```
    [HttpPost("AddRole")]
    public async Task<IActionResult> AddRole(RoleViewModel roleRequest,
                                             [FromServices] DataContext context)
    {
        if (!ModelState.IsValid) return BadRequest(ModelState);
        if (context.Roles.Any(x => x.Name.Equals(roleRequest.Name))) return BadRequest("Role duplicated");

        var roleModel = new Role
        {
            Name = roleRequest.Name,
        };
        
        await context.Roles.AddAsync(roleModel);
        await context.SaveChangesAsync();
        return Ok(roleModel);
    }
```
## Patch Change User Role

```
    [HttpPatch("ChangeUserRole")]
    public async Task<IActionResult> ChangeUserRole(int UserId,
                                                    int NewRoleId,
                                                    [FromServices] DataContext context)
    {
        if(!ModelState.IsValid) return BadRequest(ModelState);

        //Consultas
        var userModel = await context
            .Users
            .FirstOrDefaultAsync(x => x.Id == UserId);

        var roleModel = await context
            .Roles
            .AsNoTracking()
            .FirstOrDefaultAsync(x => x.Id == NewRoleId);

        //Validações
        if (userModel == null || roleModel == null)
            return StatusCode(401, "Invalid Id");

        //Verificando quantos usuarios admins temos no sistema
        var userList = await context.Users.ToListAsync();
        int adminUsers = 0;
        foreach (var item in userList)
        {
            if (item.RolesId == 2) adminUsers++;
        }

        //Verificando se sobrará pelo menos 1 admin no sistema
        if (userModel.RolesId == 2 && adminUsers <= 1) return StatusCode(400, "You must have at least 1 admin user in the database");

        userModel.Roles = roleModel;

        //Admin não pode mudar sua própria role
        if (User.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value.Equals(userModel.Id.ToString())) return StatusCode(400, "You cannot change your own role");

        
        await context.SaveChangesAsync();
        return Ok(userModel.Name + " Role changed to " + roleModel.Name);
    }
```

## Get Mostrar todos os Roles

```
[HttpGet("GetRoles")]
    public async Task<IEnumerable<Role>> GetRoles([FromServices] DataContext context)
    {
        return await context.Roles.ToListAsync();
    }
```

## Patch Edit Role

```
    [HttpPatch("EditRole")]
    public async Task<IActionResult> EditRole([FromServices] DataContext context,
                                                              Role roleRequest)
    {
        if(!ModelState.IsValid) return BadRequest(ModelState);

        var roleModel = await context.Roles.AsNoTracking().FirstOrDefaultAsync(x => x.Id == roleRequest.Id);

        //Validações
        if (roleModel == null) return NotFound("Role not found");
        if (context.Roles.Any(x => x.Name.Equals(roleRequest.Name))) return BadRequest("Role duplicated");


        context.Roles.Update(roleRequest);
        await context.SaveChangesAsync(); 
        return Ok(roleRequest);        
    }
```

## Delete Roles

```
[HttpDelete("DeleteRoles")]
    public async Task<IActionResult> DeleteRoles(int roleId,
                                                [FromServices] DataContext context)
    {
        if(!ModelState.IsValid) return BadRequest(ModelState);

        var roleModel = await context.Roles.FirstOrDefaultAsync(x => x.Id == roleId);

        //Validações
        if (roleModel == null) return NotFound("Role not found");
        if (roleModel.Name.Contains("admin")) return BadRequest("Cannot delete admin role");

        context.Roles.Remove(roleModel);
        await context.SaveChangesAsync();
        return Ok("Role " + roleModel.Name + " Deleted");
    }
```

O código está inteiro comentado e separei os blocos por #region para facilitar leitura e busca dos métodos.
&nbsp;
Qualquer dúvida, é só comentar ou entrar em contato comigo.
&nbsp;

No proximo módulo eu adicionarei:

- CRUD de Usuários
- introdução do Repository Pattern
- Divisão em 3 Projetos (Infra - Domain - API)
&nbsp;
Obrigado por lerem mais um módulo da série dessa API.

{% user mgpaixao %}