bacen / pix-api

API Pix: a API do Arranjo de Pagamentos Instantâneos Brasileiro, Pix, criado pelo Banco Central do Brasil.
https://bacen.github.io/pix-api
2.39k stars 269 forks source link

Dúvida no Client Credential Flow com JWT #119

Closed randrade86 closed 4 years ago

randrade86 commented 4 years ago

Prezados,

Pela definição, devemos nos autenticar utilizando OAuth2 com o fluxo Client Credentials, onde normalmente se faz um POST na URL de token informando grant_type=client_credentials, client_id, client_secret e opcionalmente scope do token.

Numa das integrações que estamos fazendo, um PSP definiu a seguinte autenticação OAuth2:

O método do oAuth previsto para autenticação e autorização para acesso às APIs é o Private Key JWT Client Authentication (private_key_jwt) que consiste em autenticação no fluxo client_credentials baseada em chaves (privada e pública).

Lendo um pouco sobre este fluxo, entendi que seria aplicável mais no Auth Code Flow, um fluxo que não é permitido pela API PIX, porém está sendo considerado num futuro (issue https://github.com/bacen/pix-api/issues/83)

A questão é: usar o "Private Key JWT Client Authentication (private_key_jwt)" é permitido pela API PIX?

Agradeço de antemão ao time PIX e demais interessados que continuamente participam neste repositório.

dmkamers commented 4 years ago

@randrade86 a principio a private_key_jwt não implica no fluxo a ser usado, mas sim ao método que deve ser usado pelo 'cliente' (cliente oauth, não o 'end-user') - portanto o 'cliente da api pix' - para se autenticar no 'servidor de autorização'. No caso, está especificado o uso de certificado. Vamos avaliar um pouco melhor a questão, mas pedimos que explique um pouco melhor sua preocupação se possivel.

randrade86 commented 4 years ago

Boa tarde @dmkamers,

Exemplificando bem o que temos hoje: Para autenticar com esse PSP, teremos que fazer um processo, que é gerar um JWT, assiná-lo com RSA256 (um par de chave pública e privada) e associar à um KID, finalmente gerando o JWTS. Dentro desse JWTS, o campo sub representará o client_id para o PSP em questão.

Assim, ao invés de fazermos o POST com client_id + client_secret, faremos um POST contendo o JWTS para o PSP. O PSP, então, acionará via GET uma URL cadastrada que representa o jwks.json de quem emitiu o token. Nesta URL, estão disponibilizados os KIDs e as chaves públicas que são de nossa propriedade. Mais informações na RFC 7517 e RFC 7521

Não necessariamente teremos um certificado neste processo de assinatura do JWT - o certificado seria para fecharmos o MTLS, no momento do handshake, que ocorre antes da autenticação em si. O par de chaves pública/privada é gerado por quem assinou o JWT.

Entendo que usar private key JWT é mais seguro do que um client_id e client_secret, principalmente quando falamos não apenas de PIX, mas de Open Banking também - inclusive vemos que alguns PSPs estão centralizando a autenticação do PIX junto com o Open Banking.

Problema em si, particularmente não vejo, exceto pelo maior esforço técnico necessário para implementar o JWTS e a troca de chaves públicas.

Obrigado.

dmkamers commented 4 years ago

@randrade86 é isso mesmo. 'Certificado' algumas vezes se refere a um par de chaves, como você descreveu. O que está um pouco confuso é que vejo duas interpretações e processos misturados aí.

De qualquer forma, o TLS prévio é requerido, sempre, em qualquer troca destas. Dito isto:

  1. Um dos processos é o cliente da API obter um 'token' (é um JWT mas não necessariamente este que você descreveu). Este token, que contém as autorizações de acesso à API, tem que ser assinado pelo "authorization server". Isto é uma coisa.

Num cenário 'normal', com um 'client_id/secret' oauth você consegue o 'direito de pedir um token' e com tal token acessar a API. (Note que não se espera que o 'cliente' da API tenha esse client_id/secret 'online', para solicitar tokens quando bem entender... essa não é a ideia. Se fosse assim, ele não precisaria do token...). Além disso, neste caso, o token é assinado por quem te 'deu' a autorização (portanto não faz sentido 'você' assinar este token). A vantagem de fazer isso ao invés de simplesmente passar o tradicional 'usuário/senha' de cliente é que você não 'vaza' id/secret (a não ser que esteja no próprio sistema, como acabei de dizer que não deveria ser feito), e pode 'delegar' o token para alguém (para um sistema ERP, para um POS...). Esta etapa tem correlação com o POST na url de token que você citou, mas não tenho certeza se foi o que você quis dizer.

  1. Outra coisa é você efetivamente acessar a API, passando um 'token' que obteve (e não gerou você mesmo) antes.

A especificação da API Pix até aqui não entrou nos detalhes do passo 1, mas apenas que o acesso é protegido (passo 2).

Você usar o 'private_key_jwt' significaria (suponho) que, no passo 1, você não vai usar id/secret, mas sim vai passar uma 'solicitação assinada' (aí sim, você assina com a sua chave; o PSP faz o get do seu JWKS porque precisa 'validar' sua assinatura - e assim comprovar sua identidade, pra então decidir se te dá um token ou não). Isso não muda o resultado final (2): você conseguir a posse de outro token, assinado pelo servidor de autorização, e aí sim ter o acesso à API.

Esta é a leitura que estou fazendo, e assim seria perfeitamente válido, e mais seguro realmente, desde que o PSP não confie automaticamente no que você publica no seu jwks.json. Note que a aplicação que 'pede o token' (portanto assina o seu jwt de 'pedido') vai ter que usar a respectiva chave privada, e isso é praticamente a mesma coisa que usar a credencial em si (id/secret) se não for bem implementado (deixar id/secret no código é tão ruim quanto deixar a chave privada, ou o pin do HSM).

Agora, baseado na sua explicação a seguir...

Assim, ao invés de fazermos o POST com client_id + client_secret, faremos um POST contendo o JWTS para o PSP. O PSP, então, acionará via GET uma URL cadastrada que representa o jwks.json de quem emitiu o token. Nesta URL, estão disponibilizados os KIDs e as chaves públicas que são de nossa propriedade.

... me parece que você descreveu o exemplo 4.2 da RFC7521. O exemplo remete ao fluxo da outra issue mas, foi só um exemplo... o 'ponto' do exemplo é que o 'solicitante' (de token, não da API em si) se autentica com um JWT (e não com id/secret). Aí entendo sua dúvida inicial, mas não teria com o que se preocupar, porque se trataria de outro 'flow' mesmo, o exemplo.

Mas... espero ter entendido certo.

Porque outra interpretação é que o PSP te 'cede' as credenciais (chave pública/privada) 'de authorization server', que seriam dele PSP. Ou seja, "você mesmo" estaria emitindo seus próprios tokens de autorização. Alternativamente, você mesmo cria essas chaves e apenas 'informa' ao PSP a chave pública (na verdade a URL onde ele vai obtê-la). É um processo prévio equivalente ao 'onboarding' previsto: ao invés de você receber id/secret do authorization server do PSP, é você que informa pro PSP quem é o 'authorization server' - e faz isso através da url onde estão as chaves confiáveis que vão assinar os tokens de acesso. É como se o teu 'caixa' pedisse um token para teu próprio 'backend', e depois o PSP simplesmente valida o token porque confiou previamente (no 'onbording') no teu 'authorization server' (representado pelo 'teu' key-set JWKS).

A questão é:

"... a URL cadastrada... (do) jwks ... de quem emitiu o token."

Quem emite o token? (leia-se: quem assina o token?) E que 'tipo' de token? É um token de autenticação no 'authorization server' ('private_key_kwt') - minha interpretação inicial? -> Aí faz sentido o JWKS ser 'seu', porque é apenas para o seu 'pedido de token' (enquanto 'relying party') Ou é o access token propriamente dito, para acesso à API? (a segunda interpretação) -> Aí o jwks é do 'authorization server' (você!). Neste caso é um token de acesso ao 'resource provider'.

Esta última é praticamente uma 'sacada', mas pode não 'interoperar' bem e exige muito do cliente da API (certamente não é qualquer 'pequeno cliente' que mantem um authorization server - com segurança... pense no risco de alguém obter tokens de acesso - embora a 'conta transacional' seja 'sua', o risco seria do PSP também).

randrade86 commented 4 years ago

@dmkamers

Quem emite o token? (leia-se: quem assina o token?) E que 'tipo' de token? É um token de autenticação no 'authorization server' ('private_key_kwt') - minha interpretação inicial? -> Aí faz sentido o JWKS ser 'seu', porque é apenas para o seu 'pedido de token' (enquanto 'relying party') Ou é o access token propriamente dito, para acesso à API? (a segunda interpretação) -> Aí o jwks é do 'authorization server' (você!). Neste caso é um token de acesso ao 'resource provider'.

Esta última é praticamente uma 'sacada', mas pode não 'interoperar' bem e exige muito do cliente da API (certamente não é qualquer 'pequeno cliente' que mantem um authorization server - com segurança... pense no risco de alguém obter tokens de acesso - embora a 'conta transacional' seja 'sua', o risco seria do PSP também).

Internamente, também tivemos uma discussão nesse nível, inclusive como uma expressão muito usada: "estamos falando do token ou do token para gerar token?" 🤣

Convencionamos no time o "token" como sendo o access_token e o JWTS como o token que gera token 🤣.

Voltando ao ponto principal, entendi o que você quis dizer e enxergo dois cenários distintos em termos de "private_key_jwt" :

  1. Eu, como automação comercial, no onboarding do estabelecimento comercial, forneço a minha jwks_url para que o meu cliente a cadastre no seu PSP. Desta forma, entendo que o estabelecimento delegou e confia em mim para transacionar em seu nome. Nesse cenário, eu codifico o meu JWT, assino com uma chave privada (armazenada em ambiente seguro), e envio o JWT assinado ao PSP, que fará as devidas validações (e.g. issuer, sub) e também consultará meus kids disponíveis no jwks_url

  2. Eu, como automação comercial, no onboarding do estabelecimento comercial, recebo um endpoint de responsabilidade do estabelecimento comercial, que terá a função de assinar o JWT para mim. Desta forma, a chave privada e toda infraestrutura necessária para assinar o JWT. Eu só teria o "trabalho" de obter o JWT assinado e repassar ao servidor de autenticação do PSP.

Este cenário 1 deve ocorrer em 99% dos tempos no nosso universo (automação comercial) dado a complexidade do cenário 2. Dificilmente um estabelecimento comercial terá a infraestrutura necessária para o cenário 2... talvez apenas grandes empresas, que querem garantir toda a segurança do lado deles e que utilizam múltiplas automações comerciais.

No final das contas, considero que a maneira que eu peço autenticação ao PSP via Client Credentials Flow, seja usando client_id + client_secret, seja usando o JWTs, não deve ser regulamentada pelo BCB, uma vez que já existe documentação relevante acerca das boas práticas do OAuth2.

Muito obrigado.

dmkamers commented 4 years ago

@randrade86 perfeito. Vou adotar a convenção 'token'/'jtws'! 💯 :) O cenário 1 parece ser típico mesmo, e de acordo com a 'baseline' recomendada. A propósito, o "boas práticas de segurança no oauth" citada é ótima referência mesmo, muito bem lembrado. Vamos avaliar citá-la nas recomendações vigentes. Agradecemos pelas informações compartilhadas.