Anexar cabeçalho de autorização para todas as solicitações de axios

130

Eu tenho um aplicativo react / redux que busca um token de um servidor API. Após a autenticação do usuário, gostaria de fazer com que todas as solicitações axios tivessem esse token como um cabeçalho de autorização sem ter que anexá-lo manualmente a cada solicitação na ação. Eu sou bastante novo no react / redux e não tenho certeza sobre a melhor abordagem e não estou encontrando nenhum hit de qualidade no google.

Aqui está minha configuração redux:

// actions.js
import axios from 'axios';

export function loginUser(props) {
  const url = `https://api.mydomain.com/login/`;
  const { email, password } = props;
  const request = axios.post(url, { email, password });

  return {
    type: LOGIN_USER,
    payload: request
  };
}

export function fetchPages() {
  /* here is where I'd like the header to be attached automatically if the user
     has logged in */ 
  const request = axios.get(PAGES_URL);

  return {
    type: FETCH_PAGES,
    payload: request
  };
}

// reducers.js
const initialState = {
  isAuthenticated: false,
  token: null
};

export default (state = initialState, action) => {
  switch(action.type) {
    case LOGIN_USER:
      // here is where I believe I should be attaching the header to all axios requests.
      return {
        token: action.payload.data.key,
        isAuthenticated: true
      };
    case LOGOUT_USER:
      // i would remove the header from all axios requests here.
      return initialState;
    default:
      return state;
  }
}

Meu token está armazenado na loja redux em state.session.token.

Estou um pouco perdido em como proceder. Eu tentei fazer uma instância de axios em um arquivo no meu diretório raiz e atualizar / importar em vez de de node_modules, mas não é anexar o cabeçalho quando o estado muda. Quaisquer comentários / ideias são muito apreciados, obrigado.

awwester
fonte
Onde você está armazenando o token de autorização depois que o token é recebido do servidor? localStorage?
Hardik Modha
na redux store session.token
awwester

Respostas:

203

Existem várias maneiras de fazer isso. Aqui, expliquei as duas abordagens mais comuns.

1. Você pode usar interceptores axios para interceptar quaisquer solicitações e adicionar cabeçalhos de autorização.

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    const token = store.getState().session.token;
    config.headers.Authorization =  token;

    return config;
});

2. A partir da documentação de axiosvocê pode ver que há um mecanismo disponível que permite definir o cabeçalho padrão que será enviado com cada solicitação que você fizer.

axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;

Então, no seu caso:

axios.defaults.headers.common['Authorization'] = store.getState().session.token;

Se desejar, você pode criar uma função autoexecutável que definirá o próprio cabeçalho de autorização quando o token estiver presente na loja.

(function() {
     String token = store.getState().session.token;
     if (token) {
         axios.defaults.headers.common['Authorization'] = token;
     } else {
         axios.defaults.headers.common['Authorization'] = null;
         /*if setting null does not remove `Authorization` header then try     
           delete axios.defaults.headers.common['Authorization'];
         */
     }
})();

Agora você não precisa mais anexar o token manualmente a cada solicitação. Você pode colocar a função acima no arquivo que tem a garantia de ser executado todas as vezes ( por exemplo: Arquivo que contém as rotas).

Espero que ajude :)

Hardik Modha
fonte
1
já está usando redux-persist, mas dará uma olhada no middleware para anexar o token no cabeçalho, obrigado!
awwester
1
@awwester Você não precisa de middleware para anexar o token no cabeçalho. Anexar token no cabeçalho é axios.defaults.header.common[Auth_Token] = tokentão simples quanto isso.
Hardik Modha
4
@HardikModha Estou curioso para saber como alguém pode fazer isso com Fetch API.
Rowland
@Rowland, eu acredito, você precisará escrever um wrapper sobre a API de busca para conseguir o mesmo. A resposta detalhada a essa pergunta está fora do âmbito da pergunta formulada pelo PO. Você pode fazer outra pergunta :)
Hardik Modha
2
Oi @HardikModha. Se eu usar os cabeçalhos padrão para o token definido quando quiser renovar o token, ele não poderá ser definido novamente no cabeçalho. está correto? Portanto, tenho que usar os interceptores.
beginerdeveloper
50

Se você usar "axios": versão "^ 0.17.1", você pode fazer assim:

Crie uma instância de axios:

// Default config options
  const defaultOptions = {
    baseURL: <CHANGE-TO-URL>,
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // Create instance
  let instance = axios.create(defaultOptions);

  // Set the AUTH token for any request
  instance.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token');
    config.headers.Authorization =  token ? `Bearer ${token}` : '';
    return config;
  });

Em seguida, para qualquer solicitação, o token será selecionado em localStorage e adicionado aos cabeçalhos da solicitação.

Estou usando a mesma instância em todo o aplicativo com este código:

import axios from 'axios';

const fetchClient = () => {
  const defaultOptions = {
    baseURL: process.env.REACT_APP_API_PATH,
    method: 'get',
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // Create instance
  let instance = axios.create(defaultOptions);

  // Set the AUTH token for any request
  instance.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token');
    config.headers.Authorization =  token ? `Bearer ${token}` : '';
    return config;
  });

  return instance;
};

export default fetchClient();

Boa sorte.

Llioor
fonte
@ NguyễnPhúc Com prazer, a questão toda é usar "interceptores" de axios
llioor
Esta é a melhor resposta ... para inicializar o token nos interceptores para cada solicitação! . Obrigado
james ace
45

A melhor solução para mim é criar um serviço de cliente que você instancie com seu token e o use para embrulhar axios.

import axios from 'axios';

const client = (token = null) => {
    const defaultOptions = {
        headers: {
            Authorization: token ? `Token ${token}` : '',
        },
    };

    return {
        get: (url, options = {}) => axios.get(url, { ...defaultOptions, ...options }),
        post: (url, data, options = {}) => axios.post(url, data, { ...defaultOptions, ...options }),
        put: (url, data, options = {}) => axios.put(url, data, { ...defaultOptions, ...options }),
        delete: (url, options = {}) => axios.delete(url, { ...defaultOptions, ...options }),
    };
};

const request = client('MY SECRET TOKEN');

request.get(PAGES_URL);

Nesse cliente, você também pode recuperar o token do localStorage / cookie, conforme desejar.

Kmaschta
fonte
1
E se você quiser fazer o request.get () com cabeçalhos de "tipo de aplicativo". Com sua abordagem, os cabeçalhos de defaultOptions serão substituídos pelos cabeçalhos da solicitação. Eu estou certo? Obrigado.
nipuro
9

Da mesma forma, temos uma função para definir ou excluir o token de chamadas como esta:

import axios from 'axios';

export default function setAuthToken(token) {
  axios.defaults.headers.common['Authorization'] = '';
  delete axios.defaults.headers.common['Authorization'];

  if (token) {
    axios.defaults.headers.common['Authorization'] = `${token}`;
  }
}

Sempre limpamos o token existente na inicialização e, em seguida, estabelecemos o recebido.

elQueFaltaba
fonte
5

Se você quiser chamar outras rotas de API no futuro e manter seu token na loja, tente usar o middleware redux .

O middleware pode escutar a ação de uma api e despachar solicitações de api por meio de axios de acordo.

Aqui está um exemplo muito básico:

actions / api.js

export const CALL_API = 'CALL_API';

function onSuccess(payload) {
  return {
    type: 'SUCCESS',
    payload
  };
}

function onError(payload) {
  return {
    type: 'ERROR',
    payload,
    error: true
  };
}

export function apiLogin(credentials) {
  return {
    onSuccess,
    onError,
    type: CALL_API,
    params: { ...credentials },
    method: 'post',
    url: 'login'
  };
}

middleware / api.js

import axios from 'axios';
import { CALL_API } from '../actions/api';

export default ({ getState, dispatch }) => next => async action => {
  // Ignore anything that's not calling the api
  if (action.type !== CALL_API) {
    return next(action);
  }

  // Grab the token from state
  const { token } = getState().session;

  // Format the request and attach the token.
  const { method, onSuccess, onError, params, url } = action;

  const defaultOptions = {
    headers: {
      Authorization: token ? `Token ${token}` : '',
    }
  };

  const options = {
    ...defaultOptions,
    ...params
  };

  try {
    const response = await axios[method](url, options);
    dispatch(onSuccess(response.data));
  } catch (error) {
    dispatch(onError(error.data));
  }

  return next(action);
};
Paulo. B
fonte
3

Às vezes, você obtém um caso em que algumas das solicitações feitas com axios são apontadas para endpoints que não aceitam cabeçalhos de autorização. Assim, a forma alternativa de definir o cabeçalho de autorização apenas no domínio permitido é como no exemplo abaixo. Coloque a seguinte função em qualquer arquivo executado sempre que o aplicativo React for executado, como no arquivo de rotas.

export default () => {
    axios.interceptors.request.use(function (requestConfig) {
        if (requestConfig.url.indexOf(<ALLOWED_DOMAIN>) > -1) {
            const token = localStorage.token;
            requestConfig.headers['Authorization'] = `Bearer ${token}`;
        }

        return requestConfig;
    }, function (error) {
        return Promise.reject(error);
    });

}
Karolis Ramanauskas
fonte
0

Tente fazer uma nova instância como eu fiz abaixo

var common_axios = axios.create({
    baseURL: 'https://sample.com'
});

// Set default headers to common_axios ( as Instance )
common_axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// Check your Header
console.log(common_axios.defaults.headers);

Como usá-lo

common_axios.get(url).......
common_axios.post(url).......
ugali macio
fonte
0
export const authHandler = (config) => {
  const authRegex = /^\/apiregex/;

  if (!authRegex.test(config.url)) {
    return store.fetchToken().then((token) => {
      Object.assign(config.headers.common, { Authorization: `Bearer ${token}` });
      return Promise.resolve(config);
    });
  }
  return Promise.resolve(config);
};

axios.interceptors.request.use(authHandler);

Encontrei algumas pegadinhas ao tentar implementar algo semelhante e, com base nessas respostas, foi isso que eu descobri. Os problemas que eu estava enfrentando eram:

  1. Se estiver usando axios para a solicitação para obter um token em sua loja, você precisa detectar o caminho antes de adicionar o cabeçalho. Do contrário, ele tentará adicionar o cabeçalho a essa chamada também e entrará em um problema de caminho circular. O inverso de adicionar regex para detectar as outras chamadas também funcionaria
  2. Se a loja estiver retornando uma promessa, você precisará retornar a chamada para a loja para resolver a promessa na função authHandler. A funcionalidade Async / Await tornaria isso mais fácil / mais óbvio
  3. Se a chamada para o token de autenticação falhar ou for a chamada para obter o token, você ainda deseja resolver uma promessa com o config
Bhetzie
fonte
0

O objetivo é definir o token nos interceptores para cada solicitação

import axios from "axios";
    
const httpClient = axios.create({
    baseURL: "http://youradress",
    // baseURL: process.env.APP_API_BASE_URL,
});

httpClient.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token');
    config.headers.Authorization =  token ? `Bearer ${token}` : '';
    return config;
});
James ace
fonte