Oi, Daniela!
Realmente, nesse curso não foi mostrado como usar JWT com algoritmos assimétricos de assinatura. Então, nós temos apenas um segredo (a chaveJWT
) e não dois tipos de chaves (chave pública e chave privada). Ambos os métodos são válidos e isso depende do tipo da sua aplicação.
No caso do curso, o servidor que gera os tokens é o mesmo que verifica eles, por isso podemos usar um algoritmo simétrico de assinatura (por exemplo, HMAC + SHA256), que usa apenas uma chave secreta, para assinar o token. Além disso, esse é um método mais fácil de implementar e é muito mais rápido que outros métodos.
Entretanto, se você estiver numa situação na qual um servidor que gera os tokens e um ou mais servidores diferentes que verificam os tokens, então é necessário utilizar um algoritmo assimétrico para assinatura. Os mais comuns são o RS256 (assinatura do RSA + SHA256) e ES256 (assinatura do ECDSA + SHA256). Como escolher eles? Basicamente, o RSA é mais rápido mas o ECDSA permite chaves menores, então é uma escolha que depende do seu caso. De qualquer forma, ambos os métodos são bem mais lentos e complexos que o HMAC.
Vamos ver então um exemplo de JWT com o algoritmo RS256
. Primeiro, é preciso gerar as chaves pública e privada. Para isso, podemos criar um programa generate-keys.js
como o abaixo:
const fs = require('fs');
const { generateKeyPair } = require('crypto');
// substituir 'senha super secreta' por uma senha aleatória
// e guardada em variável de ambiente
const senha = 'senha super secreta';
generateKeyPair('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: senha
}
}, (erro, chavePublica, chavePrivada) => {
fs.writeFileSync('public.pem', chavePublica);
fs.writeFileSync('private.key', chavePrivada);
}
);
Com isso, ao rodar o programa com node generate-keys.js
no seu servidor de autenticação, você vai gerar dois arquivos:
public.pem
, com a chave pública;private.key
, com a chave privada criptografada.
Assim, para gerar os tokens, seu servidor de autenticação deverá fazer algo da forma
// [...]
const fs = require('fs');
const jwt = require('jsonwebtoken');
// o arquivo 'private.key' pode não estar no mesmo diretório que o programa
const privateKey = fs.readFileSync('private.key');
// substituir 'senha super secreta' pela senha usada na geração
// das chaves e guardada em variáveis de ambiente
const senha = 'senha super secreta';
const token = jwt.sign(
payload,
{ key: privateKey, passphrase: senha},
{ algorithm: 'RS256', expiresIn: tempoExpiracao }
);
// [...]
e, para verificar o token, os outros servidores podem executar
// [...]
const fs = require('fs');
const jwt = require('jsonwebtoken');
// o modo como o serviço adquire a chave pública pode
// variar de acordo com a implementação
const publicKey = fs.readFileSync('public.pem');
const payload = jwt.verify(token, publicKey);
// [...]
É importante notar que essa é uma implementação mais complexa, principalmente pela administração adicional das chaves que é preciso ser feita. Por isso, recomendo ler esse artigo da Ping identity(em inglês) que explica as considerações adicionais de segurança que você precisará ter.
Mesmo assim, esse é o panorama geral de como usar JWT com algoritmos assimétricos de assinatura.
Se tiver mais alguma dúvida, é só falar ;)
Abraços!