DEV Community

Hernani Almeida
Hernani Almeida

Posted on

Api Rest .Net completa com JwtToken integração com api ViaCep utilizando padrão de arquitetura clean architecture - parte 3

Ferramentas necessárias:

Seguimos com a construção da nossa api, neste artigo vamos estruturar a parte de segurança de nossa api, bloqueando acesso de quem não esta autenticado ou sem autorização para acessar algum recurso.
Para realizar essa proteção vamos utilizar a autenticação via JWT JSON WEB TOKEN bora la.
Para iniciar vamos instalar as bibliotecas necessárias, conforme vimos nos artigos anteriores vamos utilizar o gerenciador de pacotes do Nuget

Image description
Dentro do gerenciador busque e instale essas duas bibliotecas

Image description

Image description

Feito isso, dentro da pasta Controllers que fica na camada de Infrastructure de nossa api (vide artigo 1 para entender a definição da arquitetura escolhida) vamos proteger nossas classes de acesso conforme imagem abaixo.

Image description

Image description

Observação: Como passamos a anotação [Authorize(Roles = "ADMIN")] acima do nome da nossa classe isso faz com que todos os endpoints de acesso sejam protegidos, atraves do JWTToken e a role de permissão concedida ao usuario, caso passemos a anotação acima de um método especifico dentro da nossa classe (conforme imagem abaixo) apenas esse método será protegido o acesso.

Image description

Feito isso e necessário mais uma configuração dentro na nossa classe Program.cs, dentro do metodo Main vamos instanciar uma variável secretKey, essa assinatura é utilizada para garantir a integridade do token, no caso, se ele foi modificado e se realmente foi gerado por você, você pode gerar um Guid para garantir a integridade dessa chave e que ninguem a conheça para decodificar seu token.

Image description
Logo acima da configuração app.UseAuthorization() instancia a config app.UseAuthentication()

Image description

Agora adicionamos a configuração para geração do token conforme imagem abaixo.

Image description

ValidateIssuer = true: Configura que na geração do token seja validado se o Issuer contido no token e igual ao que passamos na config ValidIssuer.
ValidateAudience= true: : Configura que na geração do token seja validado se o Audience contido no token e igual ao que passamos na config ValidAudience.
ValidateLifetime = true: Configura a validação de expiração do token gerado.
ValidateIssuerSigningKey: Valida se a chave secreta contida na geração do token e a mesma que passamos na variavel secretKey

Nossa classe Main ficara conforme abaixo:


using FirstApi.Application.UseCases.CasesEmployer.ConsultEmployer;
using FirstApi.Application.UseCases.CasesEmployer.DeleteEmployer;
using FirstApi.Application.UseCases.CasesEmployer.Register;
using FirstApi.Application.UseCases.CasesEmployer.UpdateEmployer;
using FirstApi.Application.UseCases.CasesUser.ConsultUser;
using FirstApi.Application.UseCases.CasesUser.DeleteUser;
using FirstApi.Application.UseCases.CasesUser.RegisterUser;
using FirstApi.Application.UseCases.CasesUser.UpdateUser;
using FirstApi.Application.UseCases.PasswordHasher;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.Data;
using FirstApi.Infrastructure.Handler;
using FirstApi.Infrastructure.Integration.ViaCep;
using FirstApi.Infrastructure.Integration.ViaCep.Refit;
using FirstApi.Infrastructure.Repositories;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Refit;
using System.Text;

namespace FirstApi
{
    public class Program
    {

        public static void Main(string[] args)
        {
            string secretKey = "e0606215-c2e4-46fe-8d73-a77e1fa4f45a";
            var builder = WebApplication.CreateBuilder(args);
            // Configure logging
            builder.Logging.ClearProviders();
            builder.Logging.AddConsole();
            builder.Logging.AddDebug();
            // Configure data base access
            builder.Services.AddDbContext<SystemDbContext>(
                options => options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
            // Add repositories to the container.
            builder.Services.AddScoped<IEmployerRepository, EmployerRepository>();
            builder.Services.AddScoped<IUserRepository, UserRepository>();
            // Add services to the container.
            // employer
            builder.Services.AddScoped<IRegisterEmployerService, RegisterEmployerService>();
            builder.Services.AddScoped<IUpdateEmployerService, UpdateEmployerService>();
            builder.Services.AddScoped<IConsultEmployerService, ConsultEmployerService>();
            builder.Services.AddScoped<IDeleteEmployerService, DeleteEmployerService>();
            // user
            builder.Services.AddScoped<IRegisterUserService, RegisterUserService>();
            builder.Services.AddScoped<IUpdateUserService, UpdateUserService>();
            builder.Services.AddScoped<IConsultUserService, ConsultUserService>();
            builder.Services.AddScoped<IDeleteUserService, DeleteUserService>();
            builder.Services.AddScoped<IPasswordHasher, PasswordHasher>();
            // client viacep
            builder.Services.AddScoped<IViaCepIntegrationService, ViaCepIntegrationService>();
            // Add client refit
            builder.Services.AddRefitClient<IViaCepIntegrationRefit>().ConfigureHttpClient(c =>
            {
                c.BaseAddress = new Uri("https://viacep.com.br");
            }
            );

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            // Config authentication
            builder.Services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                {
                    ValidateIssuer = true,
                    ValidateAudience= true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = "emprise",
                    ValidAudience = "firstApi",
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
                };
            });

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseAuthorization();

            // global error handler
            app.UseMiddleware<GlobalExceptionHandler>();

            app.MapControllers();

            app.Run();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Pronto nossa api ja esta protegida, se fizermos um request sem passar o token de acesso vamos receber um status code 401.

Image description

Vamos agora implementar um endpoint para criar um login e gerar um JsonToken para acessarmos nossa api, na nossa camada Application vamos criar um caso de uso PasswordHasher responsavel por criptografar o password do usuario e armazenar banco de dados e mais um caso de uso chamado Login conforme imagens abaixo.

Image description

Image description
IPasswordHasher

namespace FirstApi.Application.UseCases.PasswordHasher
{
    public interface IPasswordHasher
    {
        public string Hash(string password);

        public bool Verify(string passwordHash, string password);
    }
}
Enter fullscreen mode Exit fullscreen mode

PasswordHasher

using System.Security.Cryptography;

namespace FirstApi.Application.UseCases.PasswordHasher
{
    public class PasswordHasher : IPasswordHasher
    {
        private int SaltSize = 128 / 8;
        private int KeySize = 256 / 8;
        private int Iterations = 10000;
        private static HashAlgorithmName _hash = HashAlgorithmName.SHA256;
        private char Delimiter = ';'; 
        string IPasswordHasher.Hash(string password)
        {
            var salt = RandomNumberGenerator.GetBytes(SaltSize);
            var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations,_hash, KeySize);
            return string.Join(Delimiter, Convert.ToBase64String(salt), Convert.ToBase64String(hash));
        }

        bool IPasswordHasher.Verify(string passwordHash, string password)
        {
            var elements = passwordHash.Split(Delimiter);
            var salt = Convert.FromBase64String(elements[0]);
            var hash = Convert.FromBase64String(elements[1]);
            var hashInput = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, _hash, KeySize);
            return CryptographicOperations.FixedTimeEquals(hash, hashInput);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

ILoginService

namespace FirstApi.Application.UseCases.CasesAuth.Login
{
    public interface ILoginService
    {
        public LoginOutput Execute(LoginInput input);
    }
}
Enter fullscreen mode Exit fullscreen mode

LoginService

using FirstApi.Application.UseCases.PasswordHasher;
using FirstApi.Domain.Entities;
using FirstApi.Domain.Enums;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.CustomException;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;

namespace FirstApi.Application.UseCases.CasesAuth.Login
{
    public class LoginService : ILoginService
    {
        private readonly IUserRepository _userRepository;
        private readonly IPasswordHasher _passwordHasher;

        public LoginService(IUserRepository userRepository, IPasswordHasher passwordHasher)
        {
            _userRepository = userRepository;
            _passwordHasher = passwordHasher;
        }

        LoginOutput ILoginService.Execute(LoginInput input)
        {
            User user = _userRepository.FindUserByEmail(input.Email).Result;
            if (user == null || user.Password == null || user.Email == null)
            {
                throw new AppNotFoundException("Credenciais Invalidas teste");
            }

            if (_passwordHasher.Verify(user.Password, input.Password))
            {
                return new LoginOutput().Convert(user, this.generateJwtToken(user.Roles));
            }

            throw new BadHttpRequestException("Credenciais Invalidas");

        }

        private string generateJwtToken(List<Roles> roles)
        {
            string secretKey = "e0606215-c2e4-46fe-8d73-a77e1fa4f45a";
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
            var claims = new List<Claim>
            {
                new Claim("login","admin"),
                new Claim("name", "System Administrator")
            };

            claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role.ToString())));

            var token = new JwtSecurityToken(
                issuer: "emprise",
                audience: "firstApi",
                claims: claims,
                expires: DateTime.UtcNow.AddHours(2),
                signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
                );

            return new JwtSecurityTokenHandler().WriteToken(token);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

LoginInput

namespace FirstApi.Application.UseCases.CasesAuth.Login
{
    public class LoginInput
    {
        public required string Email { get; set; }
        public required string Password { get; set; }


    }
}
Enter fullscreen mode Exit fullscreen mode

LoginOutput

using FirstApi.Domain.Entities;

namespace FirstApi.Application.UseCases.CasesAuth.Login
{
    public class LoginOutput
    {
        public int Id { get; set; }
        public string? UserName { get; set; }
        public string? AccessToken { get; set; }

        internal LoginOutput Convert(User user, string token)
        {
            Id = user.Id;
            UserName = user.Name;
            AccessToken = token;
            return this;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Obs: Duvida sobre a implementação veja a primeira parte do artigo pois explico a responsabilidade de cada classe construido dentro do caso de uso.

Será necessário também mudarmos nossa entity User.cs pois precisamos adicionar uma lista de Roles para cada usuário salvo que será as permissões de acessos que esse usuário possui mesmo tendo um token JwtToken gerado.
Mude a classe User.cs para que fique assim

using FirstApi.Domain.Enums;
using FirstApi.Domain.ValueObjects;
using FirstApi.Infrastructure.Integration.ViaCep;

namespace FirstApi.Domain.Entities
{
    public class User
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Email { get; set; }
        public string? Password { get; set; }
        public List<Roles>? Roles { get; set; }
        public Endereco? Endereco { get; set; }

        public Endereco GetEndereco(ViaCepResponse response)
        {
            Endereco endereco = new Endereco();
            if(response is null)
            {
                return endereco;
            }
            endereco.Complemento = response.Complemento;
            endereco.Cep = response.Cep;
            endereco.Bairro = response.Bairro;
            endereco.Logradouro = response.Logradouro;
            endereco.Localidade = response.Localidade;
            endereco.Cep = response.Cep;
            endereco.Unidade = response.Unidade;
            endereco.Uf = response.Uf;
            return endereco;
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Crie o enum contendo as roles de permissões que seu sistema possui conforme exemplo abaixo.

Image description
Obs: Como estamos mudando nossa entity será necessário mudar o mapeamento dessa tabela na classe UserMap.cs.

Image description

UserMap

using FirstApi.Domain.Entities;
using FirstApi.Domain.Enums;
using FirstApi.Domain.ValueObjects;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace FirstApi.Infrastructure.Data.Map
{
    public class UserMap : IEntityTypeConfiguration<User>
    {
        public void Configure(EntityTypeBuilder<User> builder)
        {
           var userRolesConverter = new ValueConverter<List<Roles>, string>(
           v => string.Join(',', v.Select(e => e.ToString())),
           v => v.Split(new[] { ',' }, StringSplitOptions.None)
           .Select(e => (Roles)Enum.Parse(typeof(Roles), e)).ToList());
            builder.HasKey(x => x.Id);
            builder.Property(x => x.Name).IsRequired().HasMaxLength(255);
            builder.Property(x => x.Email).IsRequired();
            builder.Property(x => x.Password).IsRequired();
            builder.Property(x => x.Roles).HasConversion(userRolesConverter).IsRequired();
            builder.ToTable("Users")
                .OwnsOne(x => x.Endereco, x =>
                {
                    x.Property(a => a.Unidade)
                    .HasColumnName("unidade")
                    .IsRequired();
                    x.Property(a => a.Complemento)
                    .HasColumnName("complemento")
                    .IsRequired();
                    x.Property(a => a.Cep)
                    .HasColumnName("cep")
                    .IsRequired();
                    x.Property(a => a.Bairro)
                    .HasColumnName("bairro")
                    .IsRequired();
                    x.Property(a => a.Logradouro)
                    .HasColumnName("logradouro")
                    .IsRequired();
                    x.Property(a => a.Uf)
                    .HasColumnName("uf")
                    .IsRequired();
                    x.Property(a => a.Localidade)
                    .HasColumnName("localidade")
                    .IsRequired();
                });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Feito isso, para facilitar, apague todas as tabelas do seu banco de dados e exclua a pasta ´Migrations´ do seu projeto, apos isso rode os seguintes comandos em ordem dentro do console gerenciador de pacotes.

1- Add-Migration InitialDB -Context <nome do contexto a ser migrado>
2- Update-Database -Context <nome do contexto a ser migrado>
Enter fullscreen mode Exit fullscreen mode

Image description

Como citamos nos artigos anteriores, por ter construído nossa app utilizando o padrão Clean Architecture e o conceito de domínio não amenico, nos traz uma facilidade de mudar alguma logica em nossa api. Precisamos mudar a logica de salvar o usuário em nosso banco de dados, pois e necessário agora salvar também as Roles de acesso que esse usuário terá para acessar nossa aplicação, para isso vamos fazer apenas mudanças na classe de input e output desse caso de uso, que ficara assim.
RegisterUserInput

using FirstApi.Application.CustomValidations;
using FirstApi.Application.UseCases.PasswordHasher;
using FirstApi.Domain.Entities;
using FirstApi.Domain.Enums;
using FirstApi.Infrastructure.Integration.ViaCep;
using System.ComponentModel.DataAnnotations;

namespace FirstApi.Application.UseCases.CasesUser.RegisterUser
{
    public class RegisterUserInput
    {
        public string? Name { get; set; }

        [EmailAddress(ErrorMessage = "Invalid email address.")]
        public required string Email { get; set; }
        [Password(ErrorMessage = "Password must be at least 8 characters long and contain an uppercase letter, a lowercase letter, a number, and a special character.")]
        public string? Password { get; set; }
        public List<Roles>? Roles { get; set; }
        public string? Cep { get; set; }

        public User Convert(IPasswordHasher hasher, ViaCepResponse response)
        {

            User user = new User();
            user.Name = Name;
            user.Password = hasher.Hash(Password);
            user.Email = Email;
            user.Roles = Roles;
            user.Endereco = user.GetEndereco(response);
            return user;
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

RegisterUserOutput

using FirstApi.Domain.Entities;
using FirstApi.Domain.ValueObjects;

namespace FirstApi.Application.UseCases.CasesUser.RegisterUser
{
    public class RegisterUserOutput
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Email { get; set; }
        public Endereco? Endereco { get; set; }

        public List<string>? Roles { get; set; }

        public RegisterUserOutput Convert(User user)
        {
            Id = user.Id;
            Name = user.Name;
            Email = user.Email;
            Endereco = user.Endereco;
            Roles = user.Roles.Select(x => x.ToString()).ToList();
            return this;
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Pronto, feito isso vamos implementar agora na camada de acesso externo de nossa aplicação um controller para fazer a autenticação/autorização do usuário.

Image description

AuthController

using FirstApi.Application.UseCases.CasesAuth.Login;
using Microsoft.AspNetCore.Mvc;

namespace FirstApi.Infrastructure.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController
    {
        private ILoginService _loginService;

        public AuthController(ILoginService loginService)
        {
            _loginService = loginService;
        }

        [HttpPost]
        public LoginOutput Login(LoginInput login)
        {
            return _loginService.Execute(login);
        }

    }
}
Enter fullscreen mode Exit fullscreen mode

Como adicionamos interfaces de services e necessário configurar a inejçao de dependência destes na nossa classe Program.cs e também adicionar uma configuração para que o swagger entenda o fluxo de autenticação da nossa api. Sua classe Program.cs devera ficar assim.


using FirstApi.Application.UseCases.CasesAuth.Login;
using FirstApi.Application.UseCases.CasesEmployer.ConsultEmployer;
using FirstApi.Application.UseCases.CasesEmployer.DeleteEmployer;
using FirstApi.Application.UseCases.CasesEmployer.Register;
using FirstApi.Application.UseCases.CasesEmployer.UpdateEmployer;
using FirstApi.Application.UseCases.CasesUser.ConsultUser;
using FirstApi.Application.UseCases.CasesUser.DeleteUser;
using FirstApi.Application.UseCases.CasesUser.RegisterUser;
using FirstApi.Application.UseCases.CasesUser.UpdateUser;
using FirstApi.Application.UseCases.PasswordHasher;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.Data;
using FirstApi.Infrastructure.Handler;
using FirstApi.Infrastructure.Integration.ViaCep;
using FirstApi.Infrastructure.Integration.ViaCep.Refit;
using FirstApi.Infrastructure.Repositories;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Refit;
using System.Text;

namespace FirstApi
{
    public class Program
    {

        public static void Main(string[] args)
        {
            string secretKey = "e0606215-c2e4-46fe-8d73-a77e1fa4f45a";
            var builder = WebApplication.CreateBuilder(args);
            // Configure logging
            builder.Logging.ClearProviders();
            builder.Logging.AddConsole();
            builder.Logging.AddDebug();
            // Configure data base access
            builder.Services.AddDbContext<SystemDbContext>(
                options => options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
            // Add repositories to the container.
            builder.Services.AddScoped<IEmployerRepository, EmployerRepository>();
            builder.Services.AddScoped<IUserRepository, UserRepository>();
            // Add services to the container.
            // employer
            builder.Services.AddScoped<IRegisterEmployerService, RegisterEmployerService>();
            builder.Services.AddScoped<IUpdateEmployerService, UpdateEmployerService>();
            builder.Services.AddScoped<IConsultEmployerService, ConsultEmployerService>();
            builder.Services.AddScoped<IDeleteEmployerService, DeleteEmployerService>();
            // user
            builder.Services.AddScoped<IRegisterUserService, RegisterUserService>();
            builder.Services.AddScoped<IUpdateUserService, UpdateUserService>();
            builder.Services.AddScoped<IConsultUserService, ConsultUserService>();
            builder.Services.AddScoped<IDeleteUserService, DeleteUserService>();
            builder.Services.AddScoped<IPasswordHasher, PasswordHasher>();
            builder.Services.AddScoped<ILoginService,LoginService>();
            // client viacep
            builder.Services.AddScoped<IViaCepIntegrationService, ViaCepIntegrationService>();
            // Add client refit
            builder.Services.AddRefitClient<IViaCepIntegrationRefit>().ConfigureHttpClient(c =>
            {
                c.BaseAddress = new Uri("https://viacep.com.br");
            }
            );

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen(sw =>
            {
                sw.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo {
                    Title = "ApiFirst", Version = "v1"
                });
                var securitySchema = new OpenApiSecurityScheme
                {
                    Name = "Jwt Authentication",
                    Description = "Enter with your Jwt Bearer Token",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.Http,
                    Scheme = "Bearer",
                    Reference = new OpenApiReference
                    {
                        Id = JwtBearerDefaults.AuthenticationScheme,
                        Type = ReferenceType.SecurityScheme
                    }
                };
                sw.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, securitySchema);
                sw.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    { securitySchema, new string[] { } }
                });
            });

            // Config authentication
            builder.Services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                {
                    ValidateIssuer = true,
                    ValidateAudience= true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = "emprise",
                    ValidAudience = "firstApi",
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
                };
            });

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseAuthorization();

            // global error handler
            app.UseMiddleware<GlobalExceptionHandler>();

            app.MapControllers();

            app.Run();
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Pronto, agora e só rodar a aplicação e testarmos a parte de segurança no swagger.

Teste usuario com token mas sem role de acesso
Dados usuarios

Image description

Gerando token usuario id 3, sem role de Admin

Image description

Image description

Request endpoint lista usuários, necessário role de Admin para acesso
Retorno statuscode 403, ou seja estamos autenticado porem nao autorizados a acessar o recurso.
Image description

Teste usuario com token e role de acesso
Vamos utilizar o mesmo token gerado com user id = 3, que possui a role USERCOMMON solicitada para buscar os dados de Employers.

Image description

Request endpoint lista employers, necessário role de USERCOMMON para acesso
Retorno statuscode 200, ou seja estamos autenticado e autorizados a acessar o recurso.

Image description

Finalizamos por aqui galerinha, deixe seu feedback por favor sobre o artigo e deixo aqui minhas redes sociais para quem quiser me adicionar e trocar uma ideia sobre desenvolvimento de softwares para aprendermos juntos, ate a próxima.

linkedin
github

Top comments (0)