javascript - vanilla - patch axios
Interceptores de Axios e inicio de sesión asíncrono. (2)
Estoy implementando la autenticación de token en mi aplicación web. Mi access token
caduca cada N minutos y luego se usa un refresh token
para iniciar sesión y obtener un nuevo access token
.
Yo uso Axios para todas mis llamadas a la API. Tengo un interceptor configurado para interceptar 401
respuestas.
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => { setTokens(success.access_token, success.refresh_token) },
error => { console.log(''Refresh login error: '', error) }
)
err.config.__isRetryRequest = true
err.config.headers.Authorization = ''Bearer '' + getAccessToken()
return axios(err.config);
}
throw err
})
Básicamente, cuando intercepto una respuesta 401, quiero iniciar sesión y luego reintentar la solicitud original rechazada con los nuevos tokens. Mi función setAccessToken()
llama a setAccessToken()
en su bloque de then
. Pero el problema es que el bloque de ese then
ocurre más tarde que el getAccessToken()
en el interceptor, por lo que el reintento ocurre con las credenciales caducadas.
getAccessToken()
y getRefreshToken()
simplemente devuelven los tokens existentes almacenados en el navegador (administran el almacenamiento local, las cookies, etc.).
¿Cómo me aseguraría de que las declaraciones no se ejecuten hasta que se devuelva una promesa?
(Aquí hay un problema correspondiente en github: https://github.com/mzabriskie/axios/issues/266 )
Podría hacerlo en la solicitud en lugar de la respuesta, y probablemente estaría más limpio, ya que evitaría golpear el servidor cuando el token de acceso haya caducado. Copiando de este artículo :
function issueToken() {
return new Promise((resolve, reject) => {
return client({
...
}).then((response) => {
resolve(response);
}).catch((err) => {
reject(err);
});
});
}
client.interceptors.request.use((config) => {
let originalRequest = config;
if (tokenIsExpired && path_is_not_login) {
return issueToken().then((token) => {
originalRequest[''Authorization''] = ''Bearer '' + token;
return Promise.resolve(originalRequest);
});
}
return config;
}, (err) => {
return Promise.reject(err);
});
Solo usa otra promesa: D
axios.interceptors.response.use(undefined, function (err) {
return new Promise(function (resolve, reject) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => {
setTokens(success.access_token, success.refresh_token)
err.config.__isRetryRequest = true
err.config.headers.Authorization = ''Bearer '' + getAccessToken();
axios(err.config).then(resolve, reject);
},
error => {
console.log(''Refresh login error: '', error);
reject(error);
}
);
}
throw err;
});
});
Si su entorno no cumple con las promesas, use polyfill, por ejemplo, https://github.com/stefanpenner/es6-promise
Pero, puede ser mejor reescribir getRefreshToken para devolver una promesa y luego simplificar el código
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
return getRefreshToken()
.then(function (success) {
setTokens(success.access_token, success.refresh_token) ;
err.config.__isRetryRequest = true;
err.config.headers.Authorization = ''Bearer '' + getAccessToken();
return axios(err.config);
})
.catch(function (error) {
console.log(''Refresh login error: '', error);
throw error;
});
}
throw err;
});
Demostración https://plnkr.co/edit/0ZLpc8jgKI18w4c0f905?p=preview