Neste artigo vamos aprender sobre a estratégia de Refresh Tokens com NestJS. Quando utilizamos tokens de acesso usando o padrão JWT para autenticação, estes tokens possuem um tempo de validade por motivos de segurança, desta forma mitigando o risco do uso de um token por usuários mal intencionados.
Porém temos alguns pontos que merecem atenção: quando este token é expirado, é necessário efetuar o login novamente para ter um novo token de acesso. Além da questão de usabilidade onde o usuário teria que efetuar login várias vezes em um determinado sistema, também tem outro ponto de seguraça, onde o usuário e senha ficariam expostos nas requisições.
Uma estratégia para que não seja necessário efetuar o login novamente e que evita o tráfego de dados sensíveis nas requisições é o uso de refresh tokens. Com eles o servidor substitui o token expirado por um novo token de acesso utilizando um refresh token para gerar um novo token.
Curso Node.js - Fundamentos
Conhecer o cursoVamos ver como podemos desenvolver esse recurso, para isto vamos utilizar a base de um exemplo já utilizado no artigo Autenticação: Protegendo Rotas com NestJS e Passport, que pode ser baixado neste repositório do github.
No momento, quando efetuado o login é gerado o token principal, conforme abaixo:
Uma observação imporante é que a estratégia de refresh token pode ser implementanda de diversas maneiras. No caso abordando por este artigo estamos utilizando os Tokens JWT. É uma caracteristica do Token JWT ter um tempo determinado de validade, portanto, ele expira, e não é diferente com o Refresh Token, porém, o Refresh Token deve ter seu tempo de validade maior que o token de acesso, que podemos configurar no momento da criação deste token.
Portanto, agora vamos gerar também o refresh token além do access token. Vale ressaltar que o refresh token deve ter um tempo de expiração maior que o token de acesso, contudo, neste caso do artigo que estamos abordando Tokens JWT para o refresh token. Existem outras estratégias que podem ser utilizadas abordando refresh tokens.
Gerando Refresh Token com NestJS
Primeiramente, no arquivo auth.service.ts
atualize o método gerarToken()
para gerar o refresh token, neste caso alteramos o tempo de expiração e a secret key para aumentar a segurança:
async gerarToken(payload: User) {
const accessToken = this.jwtService.sign(
{ email: payload.email },
{
secret: 'sua-chave',
expiresIn: '30s',
},
);
const refreshToken = this.jwtService.sign(
{ email: payload.email },
{
secret: 'sua-chave-refresh',
expiresIn: '60s',
},
);
return { access_token: accessToken, refresh_token: refreshToken };
}
Ao efetuar o login teremos o seguinte retorno:
Veja que agora temos dois tokens, o access_token
que será utilizado para autenticar as requisições, e o refresh_token
que será utilizado para gerar o novo par de tokens. Lembrando que neste caso o front-end que fará o gerenciamento desses tokens.
Desenvolvendo rota refresh e verificando validade do token
O próximo passo será desenvolver uma rota que retorne os novos tokens baseado no refrest_token
. Portanto, criaremos essa nova rota no arquivo auth.controller.ts
, da seguinte maneira:
@Post('auth/refresh')
reautenticar(@Body() body) {
return this.authService.reautenticar(body); //este método será implementado abaixo, portanto é esperado que de erro.
}
Agora é necessário implementar o método reautenticar()
no auth.service.ts
, no caso deve-se passar o token pelo corpo da requisição. Lembrando que é uma requisição POST, confirmar se o token é válido, se for retornar o novo par de tokens.
async reautenticar(body) {
const payload: User = await this.verificarRefrestToken(body); ////este método também será implementado abaixo
return this.gerarToken(payload);
}
Repare que criamos uma constante payload
sendo um usuário, pois precisamos verificar se o token é referente a um usuário existente para ser autenticado e também verificar a secret key. Para isso é criado o método verificarRefreshToken()
, que irá retornar este usuário caso ele seja válido:
private async verificarRefreshToken(body) {
const refreshToken = body.refresh_token;
if (!refreshToken) {
throw new NotFoundException('Usuário não encontrado');
}
const email = this.jwtService.decode(refreshToken)['email'];
const usuario = await this.usersService.findOneByEmail(email);
if (!usuario) {
throw new NotFoundException('Usuário não encontrado');
}
try {
this.jwtService.verify(refreshToken, {
secret: 'sua-chave-refresh',
});
return usuario;
} catch (err) {
if (err.name === 'JsonWebTokenError') {
throw new UnauthorizedException('Assinatura Inválida');
}
if (err.name === 'TokenExpiredError') {
throw new UnauthorizedException('Token Expirado');
}
throw new UnauthorizedException(err.name);
}
}
}
Obs: O método verificarRefreshToken()
é um método privado, pois somente os métodos da própria classe que poderão chamá-lo.
Veja que criamos a constante refreshToken
pegando o refresh token do body, então usamos o método decode()
para pegar o payload do token, mais precisamente a propriedade email, buscamos na base de dados se existe um usuário com este email. Se negativo, lançamos a exceção que não há usuários, caso positivo vamos verificar se a secret key está correta utilizando o
verify()
. Se tudo ocorrer da forma esperada retornamos o usuário, caso contrário tratamos os possíveis erros.
Com o usuário podemos então retornar e reaproveitar o método gerarToken()
passando o usuário como payload.
Curso Nest.js - Fundamentos
Conhecer o cursoTestando rota refresh token
Agora vamos testar! É necessário criar uma nova requisição no insomnia com o nome refresh, lembrando que é uma requisição POST, com a seguinte URL: http://localhost:3000/auth/refresh.
- Vamos gerar os tokens fazendo o login normalmente:
- Vamos copiar o refresh token, ir na requisição refresh e passar no corpo da requisição:
Perfeito, os novos tokens estão sendo gerados a partir de um refresh token. Vale alguns pontos aqui, um deles é que o refresh token também expira, obviamente. Caso o refresh token esteja expirado, será retornado o status code 401: Unauthorized
:
Desta forma sendo necessário o usuário efetuar o login novamente para ter acesso aos novos tokens. Outro ponto que vale destaque é que na abordagem deste artigo, como alteramos a secret key no momento da criação do refresh token ele será exclusivo para gerar novos tokens. Não será possível autenticar o usuário em uma rota utilizando um refresh token, sendo necessário sempre passar o token de acesso nas rotas.
Conclusão
Neste artigo verificamos como aplicar a estratégia de refresh token com NestJS, essa estratégia é amplamente utilizada. Pois ela permite trabalharmos com autenticação de forma segura e também facilita a experiência do usuário, onde não é necessário efetuar o login repetidas vezes quando um token de acesso é expirado e evita a exposição de dados sensíveis do usuário nas requisições.
Caso tenha ficado alguma dúvida você pode acessar o repositório deste exemplo para futuras consultas neste repositório do GitHub.
Recomendo também a leitura do artigo Autenticação: Geração de Token JWT com NestJS e Passport onde é explicado sobre a geração de Tokens JWT para autenticação uttilizando o NestJS e a biblioteca Passport.
Por fim, caso queira aprender mais sobre NestJS saiba que aqui na TreinaWeb temos o curso Nest.js - Fundamentos que possui 02h07 de vídeos e um total de 18 exercícios. Conheça também nossos outros cursos de TypeScript.
Veja quais são os tópicos abordados durante o curso de Nest.js - Fundamentos:
- Conhecendo a estrutura;
- Utilizando Nest CLI
- Entendendo Rotas, Controllers e Views;;
- Conexão com banco de dados;
- Usando TypeORM;
- Template Engine.