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 .
Uso Axios para todas mis llamadas a la API. Tengo un interceptor configurado para interceptar respuestas 401 .
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 volver a intentar la solicitud original rechazada con los nuevos tokens. Mi función serviceRefreshLogin llama a setAccessToken() en su bloque then . Pero el problema es que el bloque then ocurre más tarde que 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 almacenamiento local, cookies, etc.).
¿Cómo me aseguraría de que las declaraciones no se ejecuten hasta que regrese una promesa?
(Aquí hay un problema correspondiente en github: https://github.com/mzabriskie/axios/issues/266 )
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 admite promesas, use polyfill, por ejemplo, https://github.com/stefanpenner/es6-promise
Pero, puede ser mejor reescribir getRefreshToken para devolver la 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
Podría hacerlo en la solicitud en lugar de la respuesta, y probablemente sería más limpio, ya que evitaría acceder al servidor cuando el token de acceso caduque. 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); });