Vinnicius Gomes
  • Articles
  • About
  • Reading
  • Projects
  • Companies
  • Stack
  • Contact
Menu

Oct 27th, 2021 · 4yr ago

Refresh Token com Axios Interceptors e fila de requisições

Written by Vinnicius Gomes · 3 min read

main image

Hoje eu vou te mostrar como eu resolvo o problema de refresh token nas minhas aplicações! 🤩

O problema:

A um tempo atrás eu estava com problemas de refreshToken na aplicação de um cliente, o que acontecia era que a página Dashboard chamava 3 endpoints diferentes, e quando dava erro 401 (Unauthorized), meu interceptor que criei dentro do Axios disparava 3 vezes a requisição de refreshToken ocasionando uma série de erros.

Então bora fazer isso funcionar?!

giphy

Primeiro passo, o que é Axios?

O Axios é um cliente HTTP, que funciona tanto no browser quanto em Node.Js. A biblioteca é basicamente uma API que sabe interagir tanto com XMLHttpRequest quanto com a interface HTTP do Node. Isso significa que o mesmo código utilizado para fazer requisições Ajax no browser também funciona no lado do servidor.

E agora que você já sabe o que é Axios, vamos falar sobre Interceptors

Os Interceptors são como o próprio nome diz, interceptadores. No Axios a gente consegue utiliza-los para executar alguma função antes que a Request e/ou Response seja iniciada.

Segue um exemplo:

1// Adicionando um interceptor de Request
2axios.interceptors.request.use(function (config) {
3  // Faça algo antes que a solicitação seja enviada
4  return config;
5}, function (error) {
6  // Faça algo com erro da solicitação
7  return Promise.reject(error);
8});
9// Adicionando um interceptor de Response
10axios.interceptors.response.use(function (response) {
11  // Qualquer código de status que esteja dentro de 2xx fará com que essa função seja acionada
12  // Faça algo com os dados de resposta
13  return response;
14}, function (error) {
15  // Quaisquer códigos de status que estejam fora de 2xx fazem com que esta função seja acionada
16  // Faça algo com erro de resposta
17  return Promise.reject(error);
18});
api.ts

Esse exemplo você pode encontrar na própria documentação do Axios aravés desse link.

Pronto, agora chega de enrolação, e vamos para o que interessa!

giphy

⚠️ Alerta!

Um ponto importante, é que nesse post eu vou pular as etapas de criar uma aplicação React e fazer a instalação do Axios, então caso você não saiba como fazer algum desses passos só olhar a documentação do React ou do Axios.

Continuando…

1º Primeiro passo:

Vamos criar um arquivo chamado api.ts que vai ser responsável por armazenar toda a configuração do Axios que vai ficar dessa forma:

1import axios from "axios";
2
3export function api() {
4  const api = axios.create({
5    baseURL: "http://localhost:3333",
6    headers: {
7      Authorization: Bearer your_token,
8    },
9  });
10
11  return api;
12}
api.ts

Nesse arquivo, criamos a configuração inicial para usar o Axios na aplicação.

2º Segundo passo:

Agora vamos criar o nosso interceptor que vai ser responsável por validar se houve um erro de status 401 para realizarmos o refreshToken.

1import axios, { AxiosError } from "axios";
2import { signOut } from "hooks/Auth";
3
4// Variavel para informar se está acontecendo uma requisição de refresh token
5let isRefreshing = false;
6// Variavel para armazenar a fila de requisições que falharam por token expirado
7let failedRequestQueue = [];
8
9// Tipagem dos dados de response da api de autenticação
10type AuthApiResponse = {
11  token: string;
12  refreshToken: string;
13};
14
15export function api() {
16  // Cria as configurações iniciais do Axios
17  const api = axios.create({
18    baseURL: "http://localhost:3333",
19    headers: {
20      Authorization: Bearer your_token,
21    },
22  });
23
24  // Cria um interceptor para interceptar todas as requisições que forem feitas
25  api.interceptors.response.use(
26    (response) => {
27      // Se a requisição der sucesso, retorna a resposta
28      return response;
29    },
30    (error: AxiosError) => {
31      // Se a requisição der erro, verifica se o erro é de autenticação
32      if (error.response.status === 401) {
33        // Se o erro for de autenticação, verifica se o erro foi de token expirado
34        if (error.response.data?.code === "token.expired") {
35          // Recupera o refresh token do localStorage
36          const refreshToken = localStorage.getItem("refreshToken");
37          // Recupera toda a requisição que estava sendo feita e deu erro para ser refeita após o refresh token
38          const originalConfig = error.config;
39
40          // Verifica se já existe uma request de refreshToken acontecendo
41          if (!isRefreshing) {
42            // Se não existir, inicia a requisição de refreshToken
43            isRefreshing = true;
44
45            // Faz uma requisição de refreshToken
46            api
47              .post("/refresh", {
48                refreshToken,
49              })
50              .then((response) => {
51                // Recupera os dados do response e cria o newRefreshToken por que já está sendo utilizado a variável refreshToken
52                const { token, refreshToken: newRefreshToken } =
53                  response.data as AuthApiResponse;
54
55                // Salva o token no localStorage
56                localStorage.setItem("token", token);
57                // Salva o refreshToken no localStorage
58                localStorage.setItem("refreshToken", newRefreshToken);
59
60                // Define novamente o header de autorização nas requisições
61                api.defaults.headers["Authorization"] = Bearer your_token;
62
63                // Faz todas as requisições que estavam na fila e falharam
64                failedRequestQueue.forEach((request) =>
65                  request.onSuccess(token)
66                );
67                // Limpa a fila de requisições que falharam
68                failedRequestQueue = [];
69              })
70              .catch((err) => {
71                // Retorna os erros que estão salvos na fila de requisições que falharam
72                failedRequestQueue.forEach((request) => request.onFailure(err));
73                // Limpa a fila de requisições que falharam
74                failedRequestQueue = [];
75
76                // Caso der erro desloga o usuário
77                signOut();
78              })
79              .finally(() => {
80                // Indica que a requisição de refreshToken acabou
81                isRefreshing = false;
82              });
83          }
84
85          // Usando a Promise no lugar do async await, para que a requisição seja feita após o refresh token
86          return new Promise((resolve, reject) => {
87            // Adiciona a requisição na fila de requisições que falharam com as informações necessárias para refazer a requisição novamente
88            failedRequestQueue.push({
89              // Se a requisição der sucesso, chama o onSuccess
90              onSuccess: (token: string) => {
91                // Adiciona o novo token gerado no refresh token no header de autorização
92                originalConfig.headers["Authorization"] = Bearer your_token;
93
94                // Faz a requisição novamente passando as informações originais da requisição que falhou
95                resolve(api(originalConfig));
96              },
97              // Se a requisição der erro, chama o onFailure
98              onFailure: (err: AxiosError) => {
99                // Se não for possivel refazer a requisição, retorna o erro
100                reject(err);
101              },
102            });
103          });
104        } else {
105          // Caso der erro desloga o usuário
106          signOut();
107        }
108      }
109
110      // Se não cair em nenhum if retorna um error padrão
111      return Promise.reject(error);
112    }
113  );
114
115  return api;
116}
api.ts

Todo o código com o interceptor e a nossa fila de requisição fica assim, mas calma que eu vou te explicar o que está rolando!

  • Linha 11: Criamos uma variável para saber se já está acontecendo um refreshToken em caso de multiplas requisições que retornaram erro
  • Linha 13: Criamos uma variável para armazenar todas as requisições que deram erro, criando a nossa fila de requisições
  • Linha 31: Estamos criando o nosso Interceptor
  • Linha 32: Se a requisição der sucesso, apenas retornamos o response original
  • Linha 36: Todo erro que a requisição retornar cai nessa parte
  • Linha 38 – 40: Nessas linhas estamos apenas verificando se o erro que foi retornado pela API é um erro de autenticação
  • Linha 44: Recuperamos todo a request original para refazer quando o refreshToken for concluído
  • Linha 47: Verifica se não está acontecendo um fluxo de RefreshToken
  • Linha 51: Inicia a chamada para a API de refreshToken
  • Linha 70: Depois que a requisição de refreshToken for concluída, vamos realizar todas as outras requests que estavam na fila com o novo token gerado
  • Linha 74: Estamos voltando a fila para seu estado original
  • Linha 92: Quando já está acontecendo um refreshToken, nosso código vai cair nessa linha e vai recuperar e armazenar todas as requisições que falharam e vai armazenar na nossa variável de fila para ser executado após o sucesso do refreshToken
  • Linha 112: Caso der erro, deslogamos o usuário

Esse foi um breve resumo do que cada linha está fazendo, mas todo o código está comentado, para facilitar seu entendimento!

Pronto!

Agora toda vez que você tiver um ou mais erros de 401 (Unauthorized) na sua aplicação, automaticamente vai ser feito um refreshToken e vai ser chamado novamente as requisições que falharam sem precisar atualizar a tela!

Simples né?!

giphy

Bom, é isso, espero que tenha gostado!

This post is licensed under CC BY 4.0 by the author. ·

loading...

Share:

linkedingithubxinstagrammediumproduct hunt

©2026 Vinnicius Gomes

Created in

BR