Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Axios default headers cleared after page refresh in React.js

Tags:

reactjs

axios

I am setting axios.defaults.headers.Authorization = MY_TOKEN in Login component which is rendered in Authentication component which checks if this.state.loggedin is set to true. If false it renders Login component, if true it renders UserComponent with BrowserRouter. BrowserRouter reads "/" path and navigates to Documents component. During this navigation page refreshes and axios.defaults.headers.Authorization is cleared returning value of undefined. How can I preserve axios.defaults.headers even if page is refreshed or should I initialize default headers every time router navigates to other component?

UPDATE

Added some code how rendering happens in Authentication.js

      render() {
      return (
        <UserNavigationContainer
          {...this.props}
          logout={this.onClickLogoutHandler}
        />
      );
     }

UserNavigationContainer.js renders routs (not complete code)

<BrowserRouter>
      <div>
        <UserNavigationComponent {...this.props}>
          <Switch>
            <Route
              exact
              path="/"
              component={UserSubmittedDocumentsContainer}
            />

So actually when UserNavigationContainer gets rendered it navigates to "/" and refreshes page while navigating.

like image 261
Andrius Olišauskas Avatar asked Feb 11 '19 18:02

Andrius Olišauskas


3 Answers

I had a similar experience and here is how I was able to solve it

Persist token to local storage on user login/signup: the first step was to persist user token to local storage once login/signup succeeds, you can read up on the browser's local storage API here

Move logic that sets Authorization header to a component that always renders regardless of current path (Navigation bar component in my case): next was to move the logic responsible for setting Authorization header to my Navigation bar component, by doing so, it automatically fetches the active user's token from local storage and sets the authorization header. now regardless of the component being rendered by react-router, authorization header is constantly being set, avoiding the need to do so for every other component.

PS: Moving the logic doesn't stop you from initially setting the authorization header inside the login component, it only solves the problem of doing so for every other component that gets rendered.

like image 71
Damali Avatar answered Nov 15 '22 05:11

Damali


I have encountered the same issue. I solved my problem by setting the request common headers in root files. In my case it is index.js.

setting request headers common globally

Do you notice that I am setting app_token from localStorage?

Initially, The AuthLayout renders the login component. If login success I have to redirect to the admin route.

Auth.js

Login component

So, I planned to set the headers in the login component. If login success I could able to set app_token in the request headers. All went well until I refresh the page.

Login.js

setting app_token from response

So, I set the token in localStorage and used it in the index.js to set the headers globally.

Login.js

setting localstorage

I guess this is not a better solution. Well, I could able to set the headers globally and get authenticated using the token even after the page refresh.

UPDATE:

Another simple solution for setting authorization in headers is having an interceptor.

Step 1: Create a file called interceptor.js. Create an Instance.

const axios = require("axios");
const axiosApiInstance = axios.create();

// Request interceptor for API calls
axiosApiInstance.interceptors.request.use(
  async (config) => {
    config.headers = {
      Authorization: `Bearer ${localStorage.getItem("token")}`,
    };
    return config;
  },
  (error) => {
    Promise.reject(error);
  }
);

export default axiosApiInstance;

step 2: Call your subsequent API's which require authorization headers as mentioned below.

import axiosApiInstance from "./interceptor";

let response = await axiosApiInstance.get(
      "/api/***"
    );
    return response;

Thanks

like image 31
Monish N Avatar answered Nov 15 '22 05:11

Monish N


Damali's answer is quite correct, but I also think it's worth expanding on this:

Move logic that sets Authorization header to a component that always renders regardless of current path

To be honest, it's hard to understand much about OP's project structure, because the snippets posted for some reason relate to the auth-routing logic, which isn't the question asked. But, for clarity, elaborating on the above quote:

All authentication logic, including setting the axios header, should be encapsulated in a single component (probably a React Context). It is not as simple as "setting a header and passing go": any production-level app will need to:

  • Maintain some authentication state (logged in / out?)
  • Frequently evaluate that state (expired?)
  • Perhaps maintain and evaluate more detailed login info (eg roles)
  • Manipulate routing and API requests based on the above

This is the role of an auth module.

The auth module should control the axios authentication header. This means that we are almost certainly talking about two separate modules:

  1. An HTTPs service module (contains and exports the axios instance), and
  2. An auth module

Now: as OP more-or-less observed: if the auth module simply calls the axios instance and applies a header to it upon login, that will not persist after a refresh.

The trouble with Damali's answer is that even if your auth module is always rendered (eg it is at the very top of your app), the axios configuration will nevertheless not persist on page refresh. A page refresh will force a re-render: the header will be gone.

The answer is deceptively simple: re-apply the header every time auth is required (as well as on login). There are many ways to do this, here is just one:

// apiService.js
import Axios from 'axios';
const axios = Axios.create();

export function setDefaultHeader(key, value){
    axios.defaults.headers.common[key] = value;
}
export axios;


// auth.js
import { axios, setDefaultHeader } from '../services/apiService.js';

const tokenKey = 'x-auth-token';
setDefaultHeader(tokenKey, localStorage[tokenKey]);
like image 36
defraggled Avatar answered Nov 15 '22 04:11

defraggled