Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle auth0 403 error without adding specific code everywhere (Retrofit/okhttp/RxAndroid)

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

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

My question is, how can I efficiently write this behaviour in all my networking code? I will have a couple of endpoints to talk to, and they all might return the 403.

I am thinking I should first make an interceptor that adds the JWT to all requests.

Then there should be behaviour that detects the 403, quietly does a networkcall to Auth0, retrieving the new JWT. Then the original request should be tried again, with the new JWT in its headers.

So I would prefer to have this 403 handling somewhere invisible to my other code, and definitely not have to rewrite it everywhere.

Any pointers on how to achieve this will be appreciated.

--

To be clear, I am basically looking for pointers on how to achieve this using RxAndroid Observables. When a certain Observable finds the 403, it should 'inject' a new network call.

like image 423
xorgate Avatar asked May 26 '15 14:05

xorgate


1 Answers

I solved this issue by writing an Interceptor for OkHttp. It checks the statuscode of the network call. If it's a 403, call Auth0 servers and request a new id_token. Then use this token in a new version of the original request.

To test, I wrote a little webserver that checks the TestHeader for fail or succeed and returns a 403 if it's fail.

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 an OkHttpClient which I plug into 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();
             }
         }
    )
like image 59
xorgate Avatar answered Nov 15 '22 15:11

xorgate