JWT – Segurança em APIs REST com o Magic xpi – Parte 1

Veja nesta publicação como gerar tokens JWT através do Magic xpi, para adicionar segurança aos serviços REST publicados.

JWT  é texto, popularmente chamado de token, adotado oficialmente por muitas APIs REST hoje em dia como “credencial de acesso” daquele cliente (consumidor) que está tentando acessar o serviço.

O objetivo é tanto “identificar” o cliente, quanto determinar o “escopo” dele (nível de acesso ou visibilidade àquele serviço ou dados providos por aquele serviço).

O JWT é dividido em três partes (cfe. imagem abaixo) separadas por um “.“:

  • Cabeçalho (vermelho – JSon em Base64), que basicamente define qual criptografia está sendo usada nesse token
  • Payload (rosa – JSon em Base64), que são os dados do token. Ali estarão todas as informações pertinentes (cliente, emissor, validade, escopo, etc…) que o provedor julgar necessário
  • Signature (azul – Bytes em Base64), que é a assinatura digital, geralmente aplicado o algoritmo HMAC256, HMAC384 ou HAMAC512.

O propósito é que o consumidor da API sempre envie este token no cabeçalho “Authorization” das requisições, ex:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJDbGllbnQxQG1hZ2ljc29mdHdhcmVici5jb20iLCJuYW1lIjoiVXNlcjpDbGllbnQxIiwianRpIjoiOWJiZWMzOTgtMjVkMy00ODc3LWIyMjYtOWE1ODY0MzhlMWQ2IiwiaWF0IjoiMTY4MTY3ODQ3NyIsIm5iZiI6MTY4MTY3ODQ3NywiZXhwIjoxNjgxNjgyMDc3LCJpc3MiOiJNYWdpY3hwaSBieSBNYWdpY1NvZnR3YXJlQlIiLCJhdWQiOiJVc2VyOkNsaWVudDEvR2VuZXJhbCBQdXJwb3NlIn0.ObXxXRSmTfNQ4wj4u8clvNW04rB6tq3bU8n7tYE9O-g

Assim, o provedor pode determinar se quem está consumindo é alguém devidamente autorizado, e também o que ele pode fazer (ter acesso).

O fluxo genérico do consumidor é:

  • Consumir um API que retorna o token JWT
  • Fornecer este token nas demais APIs consumidas posteriormente, enquanto ele for válido, no cabeçalho ‘Authorization

Assim que o token perder sua validade, o consumidor deve solicitar um novo e seguir adiante.

Nesta publicação, veremos a 1a etapa: como gerar e prover um token JWT através do Magic xpi.

 

Identificação Segura do Cliente

 

Primeiramente, é preciso que o cliente (consumidor) do serviço identifique-se devidamente, para então receber este token, que é uma autorização de acesso às demais APIs.

A posse deste tipo de token significa por definição, passe livre às APIs (segundo limites impostos pelo conteúdo do token)

O provedor é livre para definir este fluxo de solicitação, mas o exemplo que vamos usar neste post e amplamente adotado hoje em dia, funcionará assim:

 

  • O cliente, por ocasião de seu cadastro na plataforma do provedor do serviço, receberá duas informações exclusivas e imutáveis que são:
    • Client Application Key, que é pública e pode trafegar nas requisições HTTP aos serviços
    • Client Application Secret Key, que é privada, deve ser bem guardada pelo cliente e nunca trafegar nas requisições HTTP aos serviços

 

  • Quando o cliente consumir a API que gera (ou nega) o token JWT, ele vai prover três informações:
    • Seu “Client Application Key“, que é a informação pública e serve como sua identificação de ‘usuário’
    • Uma informação aleatória (a seu livre critério), que pode (e deve) mudar a todo instante (não se repetindo em cada chamada à API)
    • Assinatura digital dessa informação aleatória, usando a sua “Client Application Secret Key” recebida anteriormente, segundo o algoritmo definido pelo provedor da API

 

Como por exemplo (valores fictícios):

Client Application Key = Client1

Informação Aleatória = Ramdom1

Assinatura = 9c6c1ae2964ef8704da6598d5c41602f78186321fd041ddb859283506ed03465

 

De onde veio este valor de assinatura?🤔

Supondo que a informação privada (Client Application Secret Key) deste cliente (Client1) seja:

FB3F5A5F1E7A448BA7215b39d3477d19b39b6f7df4104097

a assinatura HAMAC256 produzida em cima do texto “Ramdom1“, resulta em:

9c6c1ae2964ef8704da6598d5c41602f78186321fd041ddb859283506ed03465

 

A forma como o cliente vai enviar estas três informações também é de definição livre pelo provedor da API.

Pode ser como conteúdo de um POST Body, pode ser como Query String, ou outras formas.

Neste exemplo, e seguindo alguns padrões de APIs existentes por aí, usaremos três cabeçalhos HTTP para isso:

 

Authorization: Bearer Client1
X-Authorization-Raw-Data: Ramdom1
X-Authorization-Encrypted-Data: 9c6c1ae2964ef8704da6598d5c41602f78186321fd041ddb859283506ed03465

 

Quando o provedor receber estas três informações (na sua API), ele vai usar o valor “Client1” e pesquisar em sua base de dados se este cliente/usuário existe.

Encontrando, ele carrega (recupera) o seu “Client Application Secret Key“. Com ele (CASK), aplica a assinatura digital em cima do valor “Ramdom1” e compara se o resultado deu o mesmo que o cliente enviou. Se conferir (são iguais), e não for encontrada nenhuma outra restrição de negócio para este cliente, o token JWT pode gerado e devolvido a ele para uso no acesso às demais APIs.

Se não for, nega o token. Geralmente com um retorno HTTP 401.

Esse fluxo de autorização é mais seguro que o tradicional ‘usuário+senha’, pois a senha ou o equivalente a senha (CASK) não trafega nas requisições.

 

Implementado este Fluxo no Magic xpi

 

Primeiramente, vamos habilitar nos arquivos:

  • Runtime\mgreq.ini
  • Runtime\scripts\config\mgreq.ini

as variáveis HTTP “ALL_RAW” e  “HTTP_AUTHORIZATION“:

 

Criamos então um serviço HTTP no projeto Magic xpi para prover tokens JWT :

 

Em seguida, criamos um Fluxo de Integração com uma trigger HTTP associada a este serviço HTTP.

Neste fluxo vamos capturar todos os cabeçalhos HTTP disponíveis na requisição que chegou:

 

E extrair o conteúdo daqueles três que nos interessam: “Authorization“, “X-Authorization-Raw-Data” e “X-Authorization-Encrypted-Data“:

 

O próximo passo, é validar estas informações cfe. explicado anteriormente (mais acima). Não há necessidade de detalhar uma regra de negócio neste post, mas lembre-se que ela implica em usar estas três informações que o cliente (consumidor) passou para se identificar.

Se a validação for “OK“, segue adiante para a geração do token. Senão, nega a geração do token:

 

Exemplo de resposta negativa:

 

Mas como gerar este token JWT  no Magic xpi? 🤔

 

Existe uma especificação técnica para o JWT, mas nós não iremos implementá-la.

Pois existem diversos componentes prontos tanto em Java quanto em .NET FW que produzem estes tokens, então vamos usar a capacidade que o Magic xpi tem de integrar-se nativamente a estas tecnologias/plataformas e reaproveitar o que já foi feito antes, sem reinventar.

Neste exemplo, por preferência do autor, vamos integrar com o .NET FW (como demonstrado anteriormente aqui):

 

Uma vez que tenhamos clicado em “Generate” e depois em “Edit“, temos o esqueleto de uma classe .NET à nossa disposição (aqui está sendo usado o MS Visual Studio):

 

Primeiramente, precisamos alterar o target nas propriedades do projeto de “.NET 4” para “.NET 4.6.2(ou maior):

 

Em seguida, temos de acessar o NuGet Package Manager e instalar o pacote (gratuito) System.IdentityModel.Tokens.Jwt:

 

O componente será então atualizado com uma série de novas referências:

 

Por fim, basta adicionar a regra de negócio no método “Invoke” da classe, que vai pegar dados do fluxo de integração, gerar o token, e devolver o resultado ao fluxo de integração:

Nota*: Ao invés de .NET, você poderia usar componentes Java através do Java Class Connector

 

Com o valor (token) devidamente gerado, retornamos este conteúdo como resposta à requisição feita à API:

 

E pronto! Agora temos uma API REST  Magic xpi que gera tokens JWT :

 

Baixe deste endereço o projeto exemplo Magic xpi 4.13 que foi usado neste post, e veja como é fácil implantar a segurança do JWT  em suas APIs REST.

 

Fique ligado no Blog MagicBR, para mais dicas.

Manoel Frederico Silva – Gerente de Tecnologia e Evangelista MAGIC – Magic Brasil
Manoel Frederico Silva – Evangelista MAGIC – Magic Brasil

 

Para receber os artigos do Blog Magic Brasil em primeira mão no seu email, registre-se aqui

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *