Security in microservices and API's Keycloak / Auth0

Implementando segurança em Microservices utilizando OpenID Connect / OAuth 2.0 / JWT na proteção de páginas web e API’s.

Durante esse cenário nós utilizaremos o adapter de security do DevPrime Stack para ativar as configurações de segurança e o adapter de services para consultar uma api interna protegida no microservices.

Esse contexto requer que você crie um serviço no Keycloak ou Auth0 com as configurações iniciais e obtenha as credenciais para utilizar nos testes.

Criando o microservices “ms-order”

Utilize o DevPrime CLI para criar um novo microservices

    dp new ms-order --state mongodb --stream rabbitmq --marketplace order --init

Portegendo o acesso a uma tela web por OpenID Connect (OIDC)

Nós utilizaremos a abordagem OpenID Connect (OIDC). Para acelerar o processo nós utilizaremos um acelerador do DevPrime CLI que incluirá uma controller privada.

    dp add web login

Após a conclusão é possivel executar o microservices e acompanhar o resultado

    .\run.ps1 ou ./run.sh (Linux, macOS)        

Protegendo acesso ao portal web

Para a proteção do acesso ao portal nós configuraremos o adapter de security com os dados do provedor de identidade (Keycloak/Auth0). Abra o arquivo appsettings.json na pasta ms-order/src/App e modifique os dados conforme o exemplo abaixo.

  "DevPrime_Security": {
    "Enable": "true",
    "Identity": {
      "Enable": "true",
      "Type": "keycloak",
      "Domain": "http://localhost:8080/auth/realms/devprime",
      "ClientId": "myclient",
      "ClientSecret": "your client secret",
      "EnableOIDC": "true",
      "AuthenticationScheme": "OpenIdConnect",
      "LogoutUri": "http://localhost:8080/auth/realms/devprime/protocol/openid-connect/logout?redirect_uri=https%3A%2F%2Flocalhost%3A5001",
      "Scopes": "openid;email"
    }
  },

Após esse passo execute a aplicação novamente e entre no link “Private” que tentará acessar uma tela web protegida conforme demonstrado abaixo e direcionará o acesso para uma tela de login.

using System.Security.Claims;
using DevPrime.Stack.Security;
namespace DevPrime.Web;
public class Account : Routes
{
    public override void Endpoints(WebApplication app)
    {
       app.MapGet("/private", [Authorize] <==

Pelo mecanismo padrão de segurança o provedor de identidade irá solicitar o usuáro e senha.

Depois da identificação o acesso ao recurso protegido é liberado.

Protegendo acesso a API

Para avançar em nosso cenário utilizaremos o DevPrime CLI para criar um novo microservices chamado de “ms-delivery”. no primiro momento nós vamos proteger a API desse novo microservices e posteriormente simularemos um acesso utilizando HTTP do “ms-order” para “ms-delivery”.

    dp new ms-payment --state mongodb --stream rabbitmq --marketplace payment --init

Nós vamos agora editar o arquivo appsettings.json disponível na pasta ms-payment/src/App para configurar o adapter web e depois o adapter security. Aletre o valor do parâmetro url para evitar conflito de portas.

  "DevPrime_Web": {
    "url": "https://localhost:5003;http://localhost:5002",
    "enable": "true",
    "enableswagger": "true",
    "PostSuccess": "201",
    "PostFailure": "500",
    "GetSuccess": "200",
    "GetFailure": "500",
    "PatchSuccess": "200",
    "PatchFailure": "500",
    "PutSuccess": "200",
    "PutFailure": "500",
    "DeleteSuccess": "200",
    "DeleteFailure": "500",
    "EnableWebLegacy": "false",
    "EnableStaticFiles": "true"
  },

Adicione a configuração de segurança para proteção da API. Para cenário de API’s deixaremos o parâmetro “EnableOIDC” como falso.

  "DevPrime_Security": {
    "Enable": "true",
    "Identity":{
      "Enable": "true",
      "Type": "keycloak",
      "Domain": "http://localhost:8080/auth/realms/devprime",
      "Audience": "myclient",
      "EnableOIDC": "false"
    }
  },

Após concluindo esse estágio o controle de segurança de acesso a API já configurado. Localize o arquivo Payment.cs na pasta ms-payment/src/Adapters/Web/ para configurar a restrição de acesso à uma rota na API. No exemplo abaixo aplicaremos no item “app.MapPost /v1/payment”.

        
        app.MapPost("/v1/payment",
          [Authorize] async (HttpContext http, IPaymentService Service, DevPrime.Web.Models.Payment.Payment command) =>
          await Dp(http).Pipeline(() => Service.Add(command.ToApplication())));
        

Execulte a aplicação “ms-payment” e tente fazer um post diretamente na API confrome o exemplo abaixo.

Após confirmar o “Post” nós teremos um erro “401 Unauthorized”

Acessando uma API protegida

Nesse momento retorne ao microservice “ms-order” e modifique a configuração no adapter services. Abra o arquivo appsettings.json em ms-order/src/App e inclua a configuração com as credenciais que utilizaremos na comunicação entre os microservices.

  "DevPrime_Services": {
    "Enable": "true",
    "Retry": "3",
    "Circuitbreak": "45",
    "Timeout": "10",
    "Connections": [
      {
        "Name": "myprivate",
        "GrantType": "client_credentials",
        "ClientSecret": "your client secret",
        "ClientID": "your client id",
        "TokenURI": "http://localhost:8080/auth/realms/devprime/protocol/openid-connect/token"
      }
    ]
  },

Para demonstrar o nosso cenário abra o arquivo OrderCreatedEventHandler.cs na pasta ms-order/src/Core/Application/EventHandlers/Order e modifique o Handle incluindo uma chamada http com o objetivo de demostrar o acesso a uma API privada no microservices “ms-payment”.


    public override dynamic Handle(OrderCreated domainEvent)
    {
        var order = domainEvent.Get<Domain.Aggregates.Order.Order>();
        Dp.State.Order.Add(order);

        // Try post to private api
        var url = "https://localhost:5003/v1/payment";
        var data = new { customerName = order.CustomerName, orderID = order.ID, value = order.Total };
        var httpParams = new HTTPParameter(url)
        {
            Content = data,
            Connection = "myprivate" // Security Token
        };

        // Post
        var result = Dp.Services.HTTP.Post(httpParams);

        Dp.Observability.Log($"Status:{result.Dp.Status}");

        return true;
    }

Agora chegou o momento de validar todo o cenário. Inicie o microservices “ms-order” e “ms-payment”. Entre no microservices “ms-order” e faça um post na API. Ao chegar no Handle o adapter de services vai obter automaticamente um token utilizando as configurações e acessar a API privada automaticamente.

Parabéns🚀. Você protegeu uma aplicação web e acessou uma API privada.

Última modificação June 1, 2022 (023d489)