Primeiro plano
Estamos passando de uma plataforma monolítica para uma arquitetura mais orientada a serviços. Estamos aplicando princípios DDD muito básicos e dividindo nosso domínio em diferentes contextos limitados. Cada domínio é distribuído e expõe um serviço por meio de uma API da web (REST).
Devido à natureza de nossos negócios, temos serviços como Reservas , Serviços , Clientes , Produtos , etc.
Também configuramos um Identity Server (baseado no Thinktecture Identity Server 3) cuja principal função é:
- Centralize a autenticação (dadas credenciais que emite tokens)
- Adicione declarações nos tokens, como: os escopos do cliente (conforme cliente, quero dizer o aplicativo que está fazendo a solicitação), identificador do cliente (conforme cliente, quero dizer a pessoa que usa o aplicativo)
Também introduzimos o papel de um API Gateway que centraliza o acesso externo aos nossos serviços. O API Gateway fornece funcionalidades que não exigem conhecimento profundo dos domínios internos, como:
- Proxy reverso: direciona solicitações recebidas para o serviço interno apropriado
- Controle de versão: uma versão do API Gateway é mapeada para diferentes versões dos serviços internos
- Autenticação: as solicitações do cliente incluem o token emitido pelo Identity Server e o API Gateway valida o token (verifique se o usuário é quem diz que é)
- Limitação: número limite de solicitações por cliente
Autorização
No que diz respeito à autorização, isso não é gerenciado no API Gateway, mas nos próprios serviços internos. No momento, estamos fazendo dois tipos principais de autorizações:
- Autorização com base nos escopos do cliente. Exemplo: um cliente (aplicativo externo consumindo nossas APIs) requer o escopo "reservas" para acessar os pontos de extremidade da API do serviço Reservas
- Autorização com base no cliente. Exemplo: somente se um cliente (pessoa física que usa o aplicativo) for participante de uma reserva pode acessar o ponto de extremidade GET / bookings no serviço de Bookings
Para poder manipular a autorização nos serviços internos, o API Gateway simplesmente encaminha o token (ao rotear a solicitação para o serviço interno), que inclui informações sobre o cliente (o aplicativo que está fazendo a solicitação) e o cliente como uma reivindicação (em casos em que uma pessoa está logada no aplicativo cliente).
Descrição do Problema
Até aqui tudo bem até introduzirmos a comunicação entre serviços (alguns serviços podem se comunicar com outros serviços para obter alguns dados).
Questão
Como devemos abordar a autorização nas comunicações entre serviços?
Opções consideradas
Para discutir as diferentes opções, usarei o seguinte cenário de exemplo:
- Temos um aplicativo externo chamado ExternalApp que acessa nossa API ( ExternalApp pode ser visto como o cliente ) para criar o fluxo de reserva
- ExternalApp precisa acessar o serviço Reservas , portanto, concedemos ao ExternalApp o escopo "reservas"
- Internamente (isso é algo completamente transparente para o ExternalApp ), o serviço de Reservas acessa o serviço de Serviços para obter os serviços padrão de uma reserva, como voos, seguros ou aluguel de carro
Ao discutir esse problema internamente, várias opções apareceram, mas não temos certeza de qual opção é a melhor:
- Quando o Bookings se comunica com os Serviços , ele deve simplesmente encaminhar o token original que ele recebeu do API Gateway (indicando que o cliente é o ExternalApp )
- Implicações: Talvez seja necessário conceder escopos ao ExternalApp que não deveriam ter sido concedidos. Exemplo: o ExternalApp pode precisar ter o escopo "reservas" e "serviços", enquanto apenas o escopo "reservas" pode ter sido suficiente
- Quando o Bookings se comunica com os Serviços , ele encaminha um token indicando que o cliente agora se tornou Bookings (em vez do ExternalApp ) + adiciona uma declaração indicando que o Bookings está representando o cliente original ExternalApp
- Ao incluir também as informações de que o cliente original é o ExternalApp, o serviço de Serviços também pode fazer lógica, como filtrar alguns serviços, dependendo do chamador original (por exemplo, para aplicativos internos, devemos retornar todas as brigas, para aplicativos externos apenas alguns)
- Os serviços não devem se comunicar (por isso, não devemos nem estar enfrentando essa pergunta)
Agradecemos antecipadamente a sua contribuição.