2015-05-26 8 views
5

Ich benutze Auth0, die mir ein JWT (Json Web Token) und ein RefreshToken gibt. Ich benutze diese JWT in den HTTP-Headern, um mit meinem Backend zu kommunizieren.Wie mit auth0 403 Fehler umzugehen, ohne überall spezifischen Code hinzuzufügen (Retrofit/okhttp/RxAndroid)

Es könnte passieren, dass der Server mir eine 403 gibt, wenn es entscheidet, dass die JWT abgelaufen ist. In diesem Fall kann ich Aut0 bitten, mir ein neues JWT mit dem refreshToken auszugeben. Das heißt, ich rufe das Auth0-Backend an, gebe es an das refreshtoken weiter und es gibt mir ein neues JWT, das ich dann in meinen Anfragen verwenden kann.

Meine Frage ist, wie kann ich dieses Verhalten in meinem gesamten Netzwerkcode effizient schreiben? Ich werde ein paar Endpunkte haben, mit denen ich reden kann, und sie könnten alle die 403.

zurückgeben Ich denke, ich sollte zuerst einen Interceptor machen, der den JWT zu allen Anfragen hinzufügt.

Dann sollte Verhalten sein, das 403 erkennt, macht ruhig einen Netzwerkcall zu Auth0, Abrufen der neuen JWT. Dann sollte die ursprüngliche Anfrage erneut versucht werden, mit dem neuen JWT in seinen Kopfzeilen.

Also würde ich lieber diese 403 Behandlung irgendwo unsichtbar zu meinem anderen Code haben, und definitiv nicht überall neu schreiben müssen.

Alle Hinweise, wie dies zu erreichen ist, werden geschätzt.

-

Um es klar, ich im Grunde bin für Hinweise, wie diese RxAndroid Observable mit zu erreichen. Wenn ein bestimmter Observable den 403 findet, sollte er einen neuen Netzwerkanruf "injizieren".

Antwort

11

ich dieses Problem gelöst, indem ein Interceptor für OkHttp zu schreiben. Es überprüft den Statuscode des Netzwerkanrufs. Wenn es sich um einen 403 handelt, rufen Sie Aut0-Server auf und fordern Sie ein neues id_token an. Verwenden Sie dieses Token dann in einer neuen Version der ursprünglichen Anforderung.

Um zu testen, habe ich ein wenig Webserver, der die TestHeader prüft für scheitern oder erfolgreich sein und gibt eine 403, wenn es versagen ist.

public class AuthenticationInterceptor implements Interceptor { 

    @Override 
    public Response intercept(Chain chain) throws IOException { 
     Request originalRequest = chain.request(); 
     Request authenticationRequest = originalRequest.newBuilder() 
       .header("TestHeader", "fail") 
       .build(); 

     Response origResponse = chain.proceed(authenticationRequest); 

     // server should give us a 403, since the header contains 'fail' 
     if (origResponse.code() == 403) { 
      String refreshToken = "abcd"; // you got this from Auth0 when logging in 

      // start a new synchronous network call to Auth0 
      String newIdToken = fetchNewIdTokenFromAuth0(refreshToken); 

      // make a new request with the new id token 
      Request newAuthenticationRequest = originalRequest.newBuilder() 
        .header("TestHeader", "succeed") 
        .build(); 

      // try again 
      Response newResponse = chain.proceed(newAuthenticationRequest); 

      // hopefully we now have a status of 200 
      return newResponse; 
     } else { 
      return origResponse; 
     } 
    } 
} 

Dann schließe ich diesen Interceptor zu einem OkHttpClient die ich in den Retrofit Adapterstecker:

// add the interceptor to an OkHttpClient 

public static OkHttpClient getAuthenticatingHttpClient() { 
    if (sAuthenticatingHttpClient == null) { 
     sAuthenticatingHttpClient = new OkHttpClient(); 
     sAuthenticatingHttpClient.interceptors().add(new AuthenticationInterceptor()); 
    } 
    return sAuthenticatingHttpClient; 
} 

// use the OkHttpClient in a Retrofit adapter 

mTestRestAdapter = new RestAdapter.Builder() 
    .setClient(new OkClient(Network.getAuthenticatingHttpClient())) 
    .setEndpoint("http://ip_of_server:port") 
    .setLogLevel(RestAdapter.LogLevel.FULL) 
    .build(); 

// call the Retrofit method on buttonclick 

ViewObservable.clicks(testNetworkButton) 
    .map(new Func1<OnClickEvent, Object>() { 
      @Override 
      public Object call(OnClickEvent onClickEvent) { 
       return mTestRestAdapter.fetchTestResponse(); 
      } 
     } 
    ) 
2

Anstatt die Token nur nach Erhalt einer 403-Antwort zu aktualisieren, können Sie die Ablaufzeit lokal überprüfen und entsprechend aktualisieren, indem Sie die Token-Anforderung exp überprüfen. Zum Beispiel this example uses the same approach in Angular. Es ist nicht spezifisch für Android, aber die Idee ist das gleiche:

jwtInterceptorProvider.tokenGetter = function(store, jwtHelper, auth) { 
    var idToken = store.get('token'); 
    var refreshToken = store.get('refreshToken'); 
    if (!idToken || !refreshToken) { 
    return null; 
    } 
    // If token has expired, refresh it and return the new token 
    if (jwtHelper.isTokenExpired(idToken)) { 
    return auth.refreshIdToken(refreshToken).then(function(idToken) { 
     store.set('token', idToken); 
     return idToken; 
    }); 
    // If not expired, return the token directly 
    } else { 
    return idToken; 
    } 
}