Já teve uma discussão dessa interesssante no fórum de front-end(https://github.com/frontendbr/forum/discussions/1471).
Vou replicar aqui algumas das respostas, mas tenho preferido por cookies:
por @vNNi
Cookie, pelo o que li atualmente, é o mais recomendado, pensando em SPA. Tem algumas configurações nos cookies que valem dar uma olhada! Segue um artigo que li esses dias exatamente sobre isso, o título tem React e Vue, mas se encaixa pra qualquer tecnologia os conceitos.
-
https://medium.com/@jcbaey/authentication-in-spa-reactjs-and-vuejs-the-right-way-e4a9ac5cd9a3;
-
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies;
por @kazzkiq
Eu sempre utilizei localStorage/sessionStorage pra armazenar os tokens JWT ao invés de Cookies.
Pra mim as maiores vantagens são:
Escopo por domínio/TLS:
Digamos que seu servidor não foi configurado corretamente e por algum motivo você não está redirecionando seu tráfego HTTP para HTTPS. Seu usuário logou via HTTPS e fecha o navegador. Ao retornar no outro dia, ele digita o seu site sem https:// no início e entra na versão HTTP. A partir desse momento a conexão não é segura e portanto o seu token seria facilmente lido no caso de um sniffing attack. Porém o localStorage (e o sessionStorage) não compartilha as informações entre as versões HTTP e HTTPS do seu site. Sendo assim, o usuário não teria como seguir logado na aplicação e poderia ser redirecionado para um login seguro direto no cliente. Esse é apenas um exemplo da vantagem de ter escopo separado para armazenamento no browser.
Com cookies também é possível fazer essa separação de escopos, mas que eu saiba não é o default, pra "ativar" esse comportamento você precisa usar a diretiva Secure.
Cookies sempre são enviados ao servidor:
Quando você define um cookie, ele vai passar a acompanhar todas as suas requisições para o servidor, mesmo quando o servidor não faz uso dele, e você não tem controle sobre isso no cliente. Isso significa que dependendo do tamanho do seu token, você pode gerar aumento tráfego e diminuição da velocidade das requisições por estar enviando um "payload" desnecessário em cada uma delas.
Faz total sentido enviar o token na hora de se comunicar com partes autenticadas da sua API. Mas e todos os outros requests da sua aplicação, realmente precisam ficar trafegando o token junto? Com localStorage você consegue adicionar manualmente o token apenas nos requests que precisar.
Facilidade de leitura com localStorage:
É curioso sobre como o JavaScript nunca implementou uma função nativa para ler cookies de forma simples. Nesse ponto, o formato de armazenamento do localStorage (key->value) é muito mais prático:
Lendo seu token do localStorage:
localStorage.getItem('meu-token');
Lendo seu token de um cookie:
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
readCookie('meu-token');
É lógico que a função de leitura de cookies precisa ser criada apenas uma vez e depois disso ser reaproveitada na aplicação. Mas o simples fato de ter que implementar um método de leitura na mão (ao invés de existir um nativo) me faz questionar se de fato Cookies foram pensados para serem lidos rotineiramente no cliente.
Riscos de Segurança?
O maior problema que eu vejo hoje ao usar localStorage é o risco de ataques XSS. Os cookies não são imunes à esse tipo de ataques por padrão, porém você pode imunizá-los com a flag HttpOnly.
Os problemas que eu vejo nesse argumento (não usar localStorage por conta de XSS) são basicamente dois:
Ignora a modernidade dos frameworks/libs front-end atuais: XSS é um ataque comum, porém batido. A maior parte dos frameworks front-end já possuem mecanismos ativados por padrão que evitam injeção e execução de código malicioso no cliente. Ao meu ver, teria que ocorrer um erro muito grave tanto na biblioteca utilizada, quando no desenvolvimento da aplicação para deixar um problema desses passar despercebido.
A alternativa (usar cookies com HttpOnly) tira uma das vantagens de utilizar tokens "assinados": Padrões como JWT permitem enviar um payload (dados úteis) dentro do próprio token. Esses dados podem então ser lidos pelo cliente para uso na aplicação. Ao utilizar cookies com a flag HttpOnly, você evita qualquer risco de XSS, porém também perde a capacidade de leitura desse token no cliente, o que torna o payload totalmente inutilizável.
Conclusão
Como eu disse, eu sempre utilizei localStorage porque as aplicações com que trabalho se encaixam nas vantagens listadas acima. Mas isso não é regra. Se você não tem controle sobre a qualidade do código da sua aplicação front-end (e por isso corre riscos de sofrer XSS), caso a maioria das suas requisições precisem sempre do token de autenticação, e caso você use um padrão de token que não tenha payloads, provavelmente faz mais sentido usar Cookies com as flags Secure e HttpOnly. Para os outros casos, eu iria de localStorage.