2016-03-09 11 views
18

Ich implementiere Token-Authentifizierung in meiner Web-App. Meine access token läuft alle N Minuten ab und als refresh token wird verwendet, um sich einzuloggen und eine neue access token zu bekommen.Axios Interzeptoren und asynchrone Anmeldung

Ich verwende Axios für alle meine API-Aufrufe. Ich habe einen Interceptor eingerichtet, um 401 Antworten abzufangen.

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 
}) 

Grundsätzlich, wie ich eine 401-Antwort abfangen, mag ich ein Login zu tun, und als die ursprüngliche abgelehnte Anforderung mit den neuen Token zu wiederholen. Meine serviceRefreshLogin Funktion ruft setAccessToken() in seinem then Block auf. Aber das Problem ist, dass der then Block passiert später als die getAccessToken() im Abfangjäger, so dass die Wiederholung mit den alten abgelaufenen Anmeldeinformationen passiert.

getAccessToken() und getRefreshToken() geben Sie einfach die vorhandenen Token zurück, die im Browser gespeichert sind (sie verwalten localStorage, Cookies usw.).

Wie würde ich sicherstellen, dass Anweisungen erst ausgeführt werden, wenn ein Versprechen zurückgegeben wird?

(Hier ist eine entsprechende Ausgabe auf GitHub: https://github.com/mzabriskie/axios/issues/266)

Antwort

14

einfach ein weiteres Versprechen verwenden: 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; 
    }); 
}); 

Wenn Ihre Umgebung verspricht polyfill verwendet nicht suport, zum Beispiel https://github.com/stefanpenner/es6-promise

Aber Es kann besser sein, getRefreshToken umzuschreiben, um Versprechen zurückzugeben und dann Code einfacher zu machen

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; 
}); 

Demo https://plnkr.co/edit/0ZLpc8jgKI18w4c0f905?p=preview

+0

Leider funktioniert das nicht als Interceptor. Wenn ich dies versuche, wird die Anfrage erst wiederholt, nachdem das ursprüngliche Versprechen abgelehnt wurde. –

+0

Ich machte plunkr https://plnr.rc/edit/0ZLpc8jgKI18w4c0f905?p=preview – ForceUser

+0

Entschuldigung, fügte eine Klarstellung hinzu - ich sehe nicht viel Sinn darin, getRefreshToken() zu einem Versprechen zu machen. Es ist eine Funktion, die Dinge lokal im Browser verwaltet - dort ist immer etwas verfügbar. Refresh-Token läuft nie ab BTW. –