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

Oct 27th, 2021 · 5yr 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
4return config;
5}, function (error) {
6// Faça algo com erro da solicitação
7return 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
13return 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
17return 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() {
4const api = axios.create({
5  baseURL: "http://localhost:3333",
6  headers: {
7    Authorization: Bearer your_token,
8  },
9});
10
11return 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 = {
11token: string;
12refreshToken: string;
13};
14
15export function api() {
16// Cria as configurações iniciais do Axios
17const 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
25api.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
115return 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