Desenvolvimento Back-end Node

Autenticação: Refresh Token com NestJS

Neste artigo vamos continuar no assunto de autenticação, abordando a estratégia de refresh token com o NestJS.

há 1 ano 2 meses

Formação Desenvolvedor Node Full-Stack
Conheça a formação em detalhes

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.

Node.js - Fundamentos
Curso Node.js - Fundamentos
Conhecer o curso

Vamos 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:

Efetuando login e gerando token de acesso

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:

gerando refresh token com NestJS

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.

Nest.js - Fundamentos
Curso Nest.js - Fundamentos
Conhecer o curso

Testando 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:

gerando token e refresh token

  • Vamos copiar o refresh token, ir na requisição refresh e passar no corpo da requisição:

gerando par de tokens a partir do refresh token

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:

Obtendo exceção de token expirado - 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.

Autor(a) do artigo

Wesley Gado
Wesley Gado

Formado em Análise e Desenvolvimento de Sistemas pelo Instituto Federal de São Paulo, atuou em projetos como desenvolvedor Front-End. Nas horas vagas grava Podcast e arrisca uns três acordes no violão.

Todos os artigos

Artigos relacionados Ver todos