Protege os teus recursos de API para comunicação entre máquinas
Aprende como utilizar OAuth 2.0 e JWT para proteger os teus recursos de API para comunicação entre máquinas.
Ao construir um projeto que envolve múltiplos serviços, a segurança dos recursos de API é uma preocupação crítica. Neste artigo, vou mostrar-te como utilizar OAuth 2.0 e JWT para proteger a comunicação entre serviços (máquina-a-máquina), e como aplicar controlo de acesso baseado em funções (RBAC) para seguir o princípio do privilégio mínimo.
Começar
Para seguir em frente, assumo que tens os seguintes pré-requisitos:
- Uma conta Logto Cloud, ou uma instância Logto auto-hospedada
- Pelo menos dois serviços que precisam de comunicar entre si
Para fins de demonstração, suponhamos que temos os seguintes serviços:
- Um serviço de carrinho de compras que fornece APIs para gerir carrinhos de compras
- Endpoint:
https://cart.example.com/api
- Endpoint:
- Um serviço de pagamentos que fornece APIs para processar pagamentos
- Endpoint:
https://payment.example.com/api
- Endpoint:
Fluxo de autenticação
Agora, o nosso serviço de carrinho precisa chamar o serviço de pagamentos para processar pagamentos. O fluxo de autenticação é o seguinte:
Alguns conceitos-chave no diagrama acima:
- JWT (RFC 7519): JSON Web Token. Veja o nosso artigo anterior para uma introdução ao JWT.
- JWK (RFC 7517): Chave Web JSON que é usada para verificar a assinatura de um JWT. Um conjunto de JWK é um conjunto de JWKs.
- Concessão "client_credentials" (RFC 6749): Um tipo de concessão no OAuth 2.0. Usa as credenciais do cliente para obter um token de acesso. Vamos demonstrar os detalhes nas próximas seções.
Cada participante no diagrama acima tem um papel para desempenhar no fluxo de autenticação:
- Serviço de Carrinho: O cliente que precisa chamar o serviço de pagamentos. Embora seja um serviço, ainda é um cliente no contexto do OAuth 2.0, e chamamos esses clientes de "aplicações máquina-a-máquina" no Logto.
- Logto: O servidor de autorização OAuth 2.0 que emite tokens de acesso.
- Serviço de Pagamentos: O recurso de API que fornece APIs para processar pagamentos.
Vamos passar pelo fluxo de autenticação passo a passo.
Configuração inicial
Para realizar o fluxo de autenticação, precisamos criar uma aplicação máquina-a-máquina (serviço de carrinho) e um recurso de API (serviço de pagamentos) no Logto.
Criar recurso de API
Como o nosso serviço de carrinho precisa estar ciente da API do serviço de pagamentos ao realizar a autenticação, precisamos criar um recurso de API primeiro. Vai ao Console Logto, clica em API resources na barra lateral esquerda, e clica em Create API resource. No diálogo aberto, oferecemos alguns tutoriais para te ajudar a começar. Podes também clicar em Continue without tutorial para ignorá-lo.
Insere o nome e o identificador da API, por exemplo, Serviço de Pagamentos
e https://payment.example.com/api
, depois clica em Create API resource.
Após criar o recurso de API, serás redirecionado para a página de detalhes. Podemos deixá-la como está por agora.
Criar aplicação máquina-a-máquina
Clica em Applications na barra lateral esquerda, e clica em Create application. No diálogo aberto, encontra o cartão Machine-to-machine, depois clica em Start building.
Insere o nome da aplicação, por exemplo, Serviço de Carrinho
, e clica em Create application. Um guia interativo será mostrado para ajudar-te a configurar a aplicação. Podes seguir o guia para entender o uso básico, ou clicar em Finish and done para ignorá-lo.
Solicitar token de acesso
Como se assume que as aplicações máquina-a-máquina são seguras (por exemplo, estão implantadas numa rede privada), podemos usar a concessão "client_credentials" do OAuth 2.0 para obter um token de acesso. Usa autenticação básica para autenticar o cliente:
- O URL do pedido é o endpoint do token da tua instância do Logto. Podes encontrá-lo e copiá-lo na aba Advanced settings da página de detalhes da aplicação máquina-a-máquina.
- O método do pedido é
POST
. - O cabeçalho
Content-Type
do pedido éapplication/x-www-form-urlencoded
. - Para o cabeçalho
Authorization
, o valor éBasic <base64(app_id:app_secret)>
, ondeapp_id
eapp_secret
são o ID da aplicação e o segredo da aplicação da aplicação máquina-a-máquina, respetivamente. Podes encontrá-los na página de detalhes da aplicação. - O corpo do pedido precisa especificar o tipo de concessão e o identificador da API. Por exemplo,
grant_type=client_credentials&resource=https://payment.example.com/api
.grant_type=client_credentials
: O valor constante para a concessão "client_credentials".resource=https://payment.example.com/api
: O identificador da API do recurso de API que o cliente deseja acessar.- Se a aplicação precisar ser autorizada com escopos (permissões), podes também especificar os escopos no corpo do pedido. Por exemplo,
scope=read:payment write:payment
. Vamos cobrir escopos mais tarde.
Aqui está um exemplo do pedido usando curl
:
Um corpo de resposta bem-sucedido seria assim:
Enviar pedido com cabeçalho de autorização
Agora temos o token de acesso, e podemos adicioná-lo ao cabeçalho Authorization
do pedido para o recurso de API. Por exemplo, se quisermos chamar a API POST /payments
do serviço de pagamentos, podemos enviar o seguinte pedido:
Validar JWT
Podes notar que o serviço de pagamentos precisa validar o JWT usando o conjunto de JWK, e pode ter um cache do conjunto de JWK local para evitar buscar o conjunto de JWK do Logto toda vez. Felizmente, devido à popularidade do JWT, existem muitas bibliotecas que podem ajudar-te a alcançar o objetivo com algumas linhas de código.
Essas bibliotecas são geralmente chamadas "jose" (JavaScript Object Signing and Encryption) ou "jsonwebtoken". Por exemplo, no Node.js podemos usar jose para validar o JWT:
Se a validação for bem-sucedida, a variável payload
será o payload decodificado do JWT. Caso contrário, um erro será lançado.
Aplicar controlo de acesso baseado em funções
Agora protegemos com sucesso a comunicação entre o serviço de carrinho e o serviço de pagamentos. No entanto, o fluxo de autenticação apenas garante que o cliente é o verdadeiro serviço de carrinho, mas não garante que o serviço de carrinho tem permissão para realizar ações no serviço de pagamentos.
Digamos que queremos permitir que o serviço de carrinho crie pagamentos, mas não leia pagamentos.
Definir permissões
No Logto, "escopos" e "permissões" são intercambiáveis. Vai à página de detalhes do recurso de API do serviço de pagamentos, e navega até à aba Permissions. Deve estar vazia agora. Clica em Create permission, insere read:payment
como o nome da permissão, e insere Read payments
como a descrição da permissão. Depois clica em Create permission.
Repete os passos acima para criar outra permissão com o nome write:payment
e a descrição Create payments
.
Criar função para máquina-a-máquina
Uma função é um grupo de permissões. No Logto, as aplicações máquina-a-máquina podem ser atribuídas funções para conceder permissões. Clica em "Roles" na barra lateral esquerda, e clica em Create role.
- Insere
checkout
como o nome da função, e insereServiço de Checkout
como a descrição da função. - Clica em Show more options. Escolhe "Machine-to-machine app role" como o tipo de função.
- Na seção "Assigned permissions", clica no ícone de seta à esquerda do nome do recurso de API (Serviço de Pagamentos), e seleciona a permissão
write:payment
. - Clica em Create role.
- Como já temos uma aplicação máquina-a-máquina (Serviço de Carrinho), podemos atribuir diretamente a função a ela no próximo passo. Marca a caixa de seleção à esquerda do nome da aplicação (Serviço de Carrinho), e clica em Assign applications.
Solicitar token de acesso com escopos
Além dos parâmetros de corpo de pedido que mencionamos em Solicitar token de acesso, podemos também especificar os escopos no corpo do pedido. Por exemplo, se quisermos solicitar a permissão write:payment
, podemos enviar o seguinte pedido:
Para solicitar múltiplos escopos, podes separá-los com espaços. Por exemplo, scope=write:payment read:payment
.
Validar escopos
Se uma ação precisar da permissão write:payment
no serviço de pagamentos, podemos validar os escopos ao afirmar que a declaração scope
do payload do JWT:
Conclusão
Se quiseres proteger o acesso ao serviço de carrinho, podes também aplicar o mesmo fluxo de autenticação. Desta vez, o serviço de carrinho é o recurso de API, e o cliente é outro serviço que precisa de acessar.
Com o Logto, os teus recursos de API estão protegidos com OAuth 2.0 e JWT, e podes seguir o princípio do privilégio mínimo ao aplicar controlo de acesso baseado em funções. Além disso, podes também usar o Logto para gerir os teus utilizadores e as suas permissões, e até integrar com fornecedores de identidade de terceiros.