[Dúvida] - Como lidar com Token no Front-End usando uma SPA de forma (quase) 100% segura?
Olá! Eu estou desenvolvendo um simples projetinho para estudos que consiste em uma API feito em Java com o Spring e um Front-End que irá consumir essa API usando React. Tudo estava ocorrendo bem, até que eu comecei a entrar no topico que (aparentemente) assombra os desenvolvedores Back-End, o tal da segurança, nesse caso um sistema de autenticação.
Eu resolvi implementar um sistema de autenticação usando tokens, mais especificamente o JWT, e está funcionando da seguinte forma:
- O usuário envia suas credenciais (username e senha);
- O Back-End faz a verificação;
- Se tudo estiver de acordo, ele irá retornar 2 Tokens, um Access Token e um Refresh Token.
O Access Token expira em alguns poucos minutos e é enviado para o Back-End sempre que o usuário precisar ser autenticado. Já o Refresh Token é usando apenas para conseguir outro access token quando o antigo expirar, ele dura alguns dias, é salvo no banco de dados e só pode haver um único refresh token em circulação. Sempre que ele é usando para gerar um novo access token, um novo refresh token vem junto e o antigo expira. (Abordagem Rotativa)
Parece bacana, né? Bem, quase. Meus problemas oficialmente começaram no momento em que eu tenho que integrar isso com o Front-End de uma maneira segura. Em Frameworks com SSR, Nextjs por exemplo, basta armazenar os tokens como http-only cookies, mas com React eu não tenho acesso a eles. As outras alternativas são usar o LocalStore, Cookies ou guardar em memória. Vamos analisar cada uma dessas alternativas juntos?
-
Caso eu escolha guardar em LocalStore (calma, por favor não me machuque, é uma suposição), meu app fica vulnerável a XSS attacks, o que pode até não parecer mas não é algo legal.
-
O segundo é guardar em Cookies, que possui mais camadas de segurança, mas ainda é vulnerável a XSS attacks, ou seja, é melhor que o LocalStore, mas não muito.
-
O terceiro é guardar em memória, que é 100% seguro e códigos javascript de terceiros não o afetam, entretanto gera um outro problema, o de usabilidade, pois tudo que é guardado na memória é apagado quando a página sofre reload, logo, o úsuario precisaria fazer login sempre que a página recarregar, o que não é uma boa opção.
Mas calma, aparentemente nem tudo está perdido, pois é para isso que serve o refresh token, então, meu plano para a implementação seria (o que talvez seja óbvio para aqueles que já lidaram com isso):
Quando o úsuario fizer login, o refresh token é guardado em Cookie/Local Storage, mas o access token, que é o principal, fica na memória, assim, sempre que o usuário perder o acesso, seja por que ele expirou ou a página foi atualizada, a aplicação ainda conseguiria manter o usuário logado, pois ela utilizaria do refresh token para conseguir outro access token!
Maravilhoso, correto? Todos os problemas resolvidos! Chora Elon Musk! Ou será que não?
Minha principal dúvida, e o que me fez criar essa publicação não tão concisa é: Mas e se o refresh token for obtido por um terceiro?
Bem, ele conseguiria gerar novos access tokens, mas o refresh token do usuário expiraria e na tentativa de acessar uma página que precisa de autenticação, o login seria requisitado, ele iria logar e isso criaria um novo refesh token e invalidaria o que estava em circulação.
Mas, e se o nosso úsuario demorasse alguns dias para logar? Então um terceiro conseguiria fazer qualquer coisa em nome do nosso úsuario até que o refresh token expirasse. Ou talvez mais simples ainda, mesmo se o usuário logasse logo em seguida, o terceiro ainda teria um access token gerado com o antigo refresh token, e ele ainda duraria alguns minutos. Isso me parece um problema de seguraça, correto?
E por fim, resumindo todo esse post: Existe uma maneira mais segura de lidar com tokens em uma SPA? Eu estou deixando alguma informação relevante passar e esse problema é só coisa da minha cabeça? Qual outra alternativa eu tenho para autenticação que resolveria esses problemas?
Obrigado se você leu até aqui, aguardo respostas!