Gu_sp
Gu_sp

Reputation: 75

Refresh tokens with axios Interceptors + React + Redux

In my application, I m using React with Redux and axios.

I am trying to refresh my JWT token if needed via axios interceptors.

I am able to intercept the requests, check if my access token is valid, and if it is expired I am able to request a new pair of tokens using my refresh token.

However, after I obtain my new access and refresh token, my app does not continue with the original request, but it stops and tries to render my components with errors (given that I am missing the response from the original request).

I believe there is some error in how I manage my async functions

I configured an axios instance as follows

in axiosHelper.js

import axios from 'axios';
import jwt_decode from "jwt-decode"
import dayjs from 'dayjs'

let authTokens =  localStorage.getItem('authTokens') ? JSON.parse(localStorage.getItem('authTokens')) : null 
const instance = axios.create({ baseURL: 'http://localhost:8000'});

instance.interceptors.request.use(async (req) => {
    if(!authTokens){
        authTokens =  localStorage.getItem('authTokens') ? 
        JSON.parse(localStorage.getItem('authTokens')) : null 
    }
    const user = jwt_decode(authTokens.access)
    const isExpired = dayjs.unix(user.exp).diff(dayjs()) < 1

    if (!isExpired) return req

    const {tokens} = await axios.post(`/refresh-url/`, {
        refresh: authTokens.refresh
    })
    console.log('refreshed1')

    const data = authTokens
    data.refresh = tokens.refresh
    data.access = tokens.access
    console.log('refreshed2')
    localStorage.setItem('authTokens',JSON.stringify(data))

    req.headers.Authorization = `Bearer ${tokens.access}`
    console.log('refreshed3')

    return req

}, error => {
    return Promise.reject(error);
});


export default instance;

In my actions I import the instance as in this example in actions.js

import axios from '../axiosHelper.js '

...(here my imports and actions)...

const action = async () =>{

const config = { headers: {
                'Content-type': 'application/json',
                Authorization: `Bearer ${token.access}` // token from the redux store
            }

const { data } = await axios.get(
            `/api/my-url/`,
            config
       )
...(send data to reducer)...
}

then in my components, I call my actions via dispatch in useEffect. in Home.js

const Home = (props) => {

    const dispatch = useDispatch()

    useEffect(() =>{
            dispatch(action())
    }, [dispatch])

    return (<my component/>)

}

I believe the problem has something to do with dispatch. If the access token is valid, then everything works fine. However, if it is expired, my interceptor will send the request to refresh the tokens (which is successful), but as soon as the tokens are returned, my component tries to render and the rest of the code in the axios interceptor is ignored (I checked this with console log ...), and the original request is not sent (I checked that it does not arrive at my backend server).

I would really appreciate some help as I cannot figure out hoe to solve the problem!

Upvotes: 2

Views: 3261

Answers (1)

Gu_sp
Gu_sp

Reputation: 75

I solved the issue by importing the redux store into my axiosHelper.js script, and then using store.dispatch(refreshaction(authTokens.refresh)) inside my axios interceptor.

Upvotes: 1

Related Questions