How to deal with auth0 403 error without adding specific code all over the world (Retrofitting / okhttp / RxAndroid)

I use Auth0, which gives me JWT (json web token) and refreshtoken. I use this JWT in the http headers to communicate with my backend.

It may happen that the server gives me 403 when it decides that the JWT has expired. In this case, I can ask Auth0 to release me a new JWT using refreshtoken. This means that I call the Auth0 server, pass it a refreshtoken, and it gives me a new JWT, which I can then use in my requests.

My question is, how can I effectively write this behavior in all network code? I will talk with several endpoints, and all of them can return 403.

I think I should first make an interceptor that adds JWT to all requests.

Then there should be a behavior that 403 detects silently making an Auth0 network call, receiving a new JWT. Then the original request should be checked again with the new JWT in its headers.

Therefore, I would prefer this 403 processing to be invisible to my other code, and certainly no need to rewrite it everywhere.

Any pointers on how to achieve this would be appreciated.

-

To be clear, I'm mostly looking for pointers on how to achieve this using RxAndroid Observables. When a specific Observable finds 403, it must β€œenter” a new network call.

+6
source share
2 answers

I solved this problem by writing Interceptor for OkHttp . It checks the status code of the network call. If it is 403, call Auth0 servers and request a new id_token. Then use this token in the new version of the original request.

To check, I wrote a small web server that checks TestHeader for failure or succeeds and returns 403 if it does not work.

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

Then I attach this Interceptor to OkHttpClient, which I connect to the Retrofit adapter:

 // 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(); } } ) 
+12
source

Instead of updating the tokens only after receiving a 403 response, you can check the expiration time locally and update accordingly by checking the exp token request. For example, this example uses the same approach in Angular . This is not typical for Android, but the idea is the same:

 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; } } 
+2
source

Source: https://habr.com/ru/post/987945/


All Articles