admin管理员组文章数量:1317731
I'm using Axios in the frontend of my project and needed a way to redirect the user to the login page in case he wanted to used expired tokens (the refresh token is invalid for the backend), I found a boilerplate code on the web that looked easy enough to adapt for my tech stack (ReactJS with no Redux):
import axios from 'axios';
// additional code that lives in its own module
const getToken = () => {
return isAuth() ? window.localStorage.getItem("token") : "";
};
const getRefreshToken = () => {
return isAuth() ? window.localStorage.getItem("refresh_token") : "";
};
const setTokens = (token, refresh) => {
window.localStorage.setItem("token", token);
window.localStorage.setItem("refresh_token", refresh);
};
// this is on my app.js file
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// Hace la solicitud de refresco de tokens
return axios.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
.then((responseData) => {
// actualiza la información de OAuth
setTokens(responseData.data.access_token, responseData.data.refresh_token);
axios.defaults.headersmon['Authorization'] = 'Bearer ' + getToken();
originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
// re-intenta la solicitud original
return axios(originalRequest);
}).catch(function (error) {
console.log(error);
setTokens(undefined, undefined);
window.location.pathname = "/login";
});
}
return Promise.reject(error);
});
The problem is that the code enters in a infinite recursion leaving the frontend unusable. if I navigate manually to /login
the recursion stops entirely.
EDIT
Here is the entire source code of the file where I just had to do the changes according to the solution accepted for my questio:
import React, { Component } from 'react';
import createHistory from 'history/createBrowserHistory';
import {Router} from 'react-router';
import Fero from './containers/Fero/Fero';
import {setTokens, getRefreshToken, getToken} from './utils/auth';
import axios from 'axios';
const myhistory = createHistory();
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// Hace la solicitud de refresco de tokens
return axios.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
.then((responseData) => {
// actualiza la información de OAuth
setTokens(responseData.data.access_token, responseData.data.refresh_token);
axios.defaults.headersmon['Authorization'] = 'Bearer ' + getToken();
originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
// re-intenta la solicitud original
return axios(originalRequest);
}).catch(function (error) {
console.log(error);
setTokens(undefined, undefined);
myhistory.replace("/login");
});
}
return Promise.reject(error);
});
class App extends Component {
render() {
return (
<Router history={myhistory}>
<Fero/>
</Router>
);
}
}
export default App;
I'm using Axios in the frontend of my project and needed a way to redirect the user to the login page in case he wanted to used expired tokens (the refresh token is invalid for the backend), I found a boilerplate code on the web that looked easy enough to adapt for my tech stack (ReactJS with no Redux):
import axios from 'axios';
// additional code that lives in its own module
const getToken = () => {
return isAuth() ? window.localStorage.getItem("token") : "";
};
const getRefreshToken = () => {
return isAuth() ? window.localStorage.getItem("refresh_token") : "";
};
const setTokens = (token, refresh) => {
window.localStorage.setItem("token", token);
window.localStorage.setItem("refresh_token", refresh);
};
// this is on my app.js file
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// Hace la solicitud de refresco de tokens
return axios.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
.then((responseData) => {
// actualiza la información de OAuth
setTokens(responseData.data.access_token, responseData.data.refresh_token);
axios.defaults.headers.mon['Authorization'] = 'Bearer ' + getToken();
originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
// re-intenta la solicitud original
return axios(originalRequest);
}).catch(function (error) {
console.log(error);
setTokens(undefined, undefined);
window.location.pathname = "/login";
});
}
return Promise.reject(error);
});
The problem is that the code enters in a infinite recursion leaving the frontend unusable. if I navigate manually to /login
the recursion stops entirely.
EDIT
Here is the entire source code of the file where I just had to do the changes according to the solution accepted for my questio:
import React, { Component } from 'react';
import createHistory from 'history/createBrowserHistory';
import {Router} from 'react-router';
import Fero from './containers/Fero/Fero';
import {setTokens, getRefreshToken, getToken} from './utils/auth';
import axios from 'axios';
const myhistory = createHistory();
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// Hace la solicitud de refresco de tokens
return axios.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
.then((responseData) => {
// actualiza la información de OAuth
setTokens(responseData.data.access_token, responseData.data.refresh_token);
axios.defaults.headers.mon['Authorization'] = 'Bearer ' + getToken();
originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
// re-intenta la solicitud original
return axios(originalRequest);
}).catch(function (error) {
console.log(error);
setTokens(undefined, undefined);
myhistory.replace("/login");
});
}
return Promise.reject(error);
});
class App extends Component {
render() {
return (
<Router history={myhistory}>
<Fero/>
</Router>
);
}
}
export default App;
Share
Improve this question
edited Dec 13, 2018 at 9:45
shackra
asked Dec 12, 2018 at 7:21
shackrashackra
3763 gold badges17 silver badges62 bronze badges
2
- You should consider using react-router for handling URL changes. – Peter Ambruzs Commented Dec 12, 2018 at 8:42
- @Peter I'm actually using React Router ponents, but, how do I use it inside the interceptor? – shackra Commented Dec 12, 2018 at 17:19
2 Answers
Reset to default 4Well, it turns out the solution to the recursion was using a different instance of axios for the token refreshment so the interceptor did not intercept the HTTP 401 response for a expired refresh token, here is the full solution:
import React, { Component } from 'react';
import createHistory from 'history/createBrowserHistory';
import {Router} from 'react-router';
import Fero from './containers/Fero/Fero';
import {setTokens, getRefreshToken, getToken} from './utils/auth';
import axios from 'axios';
const myhistory = createHistory();
const refresh = axios.create();
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && Boolean(getRefreshToken())) {
// Hace la solicitud de refresco de tokens
return refresh.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
.then((responseData) => {
// actualiza la información de OAuth
setTokens(responseData.data.access_token, responseData.data.refresh_token);
axios.defaults.headers.mon['Authorization'] = 'Bearer ' + getToken();
originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
// re-intenta la solicitud original
return axios(originalRequest);
}).catch((error) => {
setTokens("", "");
myhistory.replace("/login");
});
}
return Promise.reject(error);
});
class App extends Component {
render() {
return (
<Router history={myhistory}>
<Fero/>
</Router>
);
}
}
export default App;
the content of src/utils/auth.js
:
export const isAuth = () => {
return window.localStorage.getItem("token") && window.localStorage.getItem("refresh_token");
};
export const getToken = () => {
return isAuth() ? window.localStorage.getItem("token") : "";
};
export const getRefreshToken = () => {
return isAuth() ? window.localStorage.getItem("refresh_token") : "";
};
export const setTokens = (token, refresh) => {
window.localStorage.setItem("token", token);
window.localStorage.setItem("refresh_token", refresh);
};
It is a good problem, and I did not know about axios interceptors. Anyway, I found a solution for this problem. The main thing is that you have to wrap your interceptor setup in a function, so you can pass the history object as a parameter. And you need the history object. One possible solution is to create one and provide the same history object to the Router as well the wrapped interceptor setup.
// in app.js create a history
const history = createHistory();
// with the history object, setup your interceptor
setupInterceptor(history)
// you have to use the same history object in your
// Router ponent, if it is in a different ponent
// you could pass it in a prop
<Router history={history}>
...
</Router>
const setupInterceptor = (history) => {
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
// ...
}).catch(function (error) {
console.log(error);
setTokens(undefined, undefined);
history.push("/login");
});
}
return Promise.reject(error);
})};
本文标签: javascriptcorrectly redirect user if refresh token has expired using axiosStack Overflow
版权声明:本文标题:javascript - correctly redirect user if refresh token has expired using axios - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742017696a2414084.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论