Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Refreshing OAuth token with okhttp interceptors. All requests will wait until token refresh finished, and then will continue with the new token.
private class HttpInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//Build new request
Request.Builder builder = request.newBuilder();
builder.header("Accept", "application/json"); //if necessary, say to consume JSON
String token = settings.getAccessToken(); //save token of this request for future
setAuthHeader(builder, token); //write current token to request
request = builder.build(); //overwrite old request
Response response = chain.proceed(request); //perform request, here original request will be executed
if (response.code() == 401) { //if unauthorized
synchronized (httpClient) { //perform all 401 in sync blocks, to avoid multiply token updates
String currentToken = settings.getAccessToken(); //get currently stored token
if(currentToken != null && currentToken.equals(token)) { //compare current token with token that was stored before, if it was not updated - do update
int code = refreshToken() / 100; //refresh token
if(code != 2) { //if refresh token failed for some reason
if(code == 4) //only if response is 400, 500 might mean that token was not updated
logout(); //go to login screen
return response; //if token refresh failed - show error to user
}
}
if(settings.getAccessToken() != null) { //retry requires new auth token,
setAuthHeader(builder, settings.getAccessToken()); //set auth token to updated
request = builder.build();
return chain.proceed(request); //repeat request with new token
}
}
}
return response;
}
private void setAuthHeader(Request.Builder builder, String token) {
if (token != null) //Add Auth token to each request if authorized
builder.header("Authorization", String.format("Bearer %s", token));
}
private int refreshToken() {
//Refresh token, synchronously, save it, and return result code
//you might use retrofit here
}
private int logout() {
//logout your user
}
}
@hoda0013

This comment has been minimized.

Copy link

hoda0013 commented Jan 22, 2016

Is this an application level interceptor or a network level interceptor?

@alex-shpak

This comment has been minimized.

Copy link
Owner Author

alex-shpak commented Jan 25, 2016

It's application level interceptor

@hoda0013

This comment has been minimized.

Copy link

hoda0013 commented Feb 3, 2016

Where are you getting the httpClient instance to synchronize on?

@alex-shpak

This comment has been minimized.

Copy link
Owner Author

alex-shpak commented Feb 4, 2016

httpClient in my case is singleton instance of OkHttpClient. You can use some other object for synchronisation or singleton.

@rodriigomedeiros

This comment has been minimized.

Copy link

rodriigomedeiros commented Aug 3, 2016

Hello, you can give more details of how the implementation of its ServiceGenerator? I have serious problems making synchronous calls to RefreshToken.

@alex-shpak

This comment has been minimized.

Copy link
Owner Author

alex-shpak commented Aug 14, 2016

Hi.
I set this interceptor to okHttp instance

        Gson gson = new GsonBuilder().create();

        OkHttpClient httpClient = new OkHttpClient();
        httpClient.interceptors().add(new HttpInterceptor());

        final RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(BuildConfig.REST_SERVICE_URL)
                .setClient(new OkClient(httpClient))
                .setConverter(new GsonConverter(gson))
                .setLogLevel(RestAdapter.LogLevel.BASIC)
                .build();

        remoteService = restAdapter.create(RemoteService.class);
@buddhasaikia

This comment has been minimized.

Copy link

buddhasaikia commented Oct 25, 2016

What is that settings variable?

@alex-shpak

This comment has been minimized.

Copy link
Owner Author

alex-shpak commented Dec 6, 2016

Hi. Sorry for late response.
settings is place where you store current user token. Database for example.

@alouanemed

This comment has been minimized.

Copy link

alouanemed commented Dec 12, 2016

Are we talking here about Server Token(sent by backend) or say Facebook Oauth Token ? Thanks .

@khadkarajesh

This comment has been minimized.

Copy link

khadkarajesh commented Dec 14, 2016

How can we make refreshing access token block to execute only one time without using synchronized to okhttp instance. Can we make synchronized to other instance?

@alex-shpak

This comment has been minimized.

Copy link
Owner Author

alex-shpak commented Dec 16, 2016

@alouanemed

Are we talking here about Server Token(sent by backend) or say Facebook Oauth Token ? Thanks.

About server token, I think facebook has their own means to update tokens.

@rajeshkumarkhadka

How can we make refreshing access token block to execute only one time without using synchronized to okhttp instance. Can we make synchronized to other instance?

I think you can use some generic object for locks, but I can't be sure that it will work correct

@jaswanthm

This comment has been minimized.

Copy link

jaswanthm commented Mar 8, 2017

I have implemented this without any fuss but there is a talk that Authenticator is a better of doing it. Thoughts anyone ?

https://github.com/square/okhttp/wiki/Recipes#handling-authentication

@luckcoolla

This comment has been minimized.

Copy link

luckcoolla commented May 12, 2017

Authenticator is completely suitable tool for the token refresh action and retry the main request

@peterlazar1993

This comment has been minimized.

Copy link

peterlazar1993 commented May 29, 2017

@alex-shpak Can you give a hint on how the settings object is accessed and updated? I am struggling to understand how to update the persistence layer once the new token is generated.

@CarlosJTorres

This comment has been minimized.

Copy link

CarlosJTorres commented Jun 27, 2017

@alex-shpak thanks for sharing your knowledge. Base on your model I've built mine own. I added few details, as synchronous refresh token and so on. This is it https://gist.github.com/CarlosJTorres/057dbf4d8de3095873b15956bdf47935 . Thank you very much.

@lalitsonawane

This comment has been minimized.

Copy link

lalitsonawane commented Jul 1, 2017

@alex_shpak i have a secured json link with https. Hiw to handle it?

@alex-shpak

This comment has been minimized.

Copy link
Owner Author

alex-shpak commented Jul 26, 2017

@jaswanthm that was quite long time ago, so I don't remember why Authenticator didn't work for us. I think it was related to paralel requests.

@peterlazar1993 there are no specific requirements on persistence, the simplest I think is to use SharedPreferences

SharedPreferences settings = ...
String token = settings.getString("token")

settings.edit()
    .putString("token", newToken)
    .commit()

@lalitsonawane can you please provide more details? From what I understood you want to update token via https.
You can use Retrofit or Okhttp to call endpoint and refresh token, note that call should be synchronous.

@akshay2211

This comment has been minimized.

Copy link

akshay2211 commented Sep 5, 2017

Thnks @alex-shpak, Helped me out alot.

@Agrahyah

This comment has been minimized.

Copy link

Agrahyah commented Oct 11, 2017

Hi, suppose i want to inject my SharedPreference (where my current token is stored) using dagger. How to do so ??

@ilhamsuaib

This comment has been minimized.

Copy link

ilhamsuaib commented Oct 25, 2017

my case is token not pushed using header like above, but token is always include in every request as parameter.
and if i want to refresh token, i should call refresh token service with POST method. how can i do that @alex-shpak?

@paragones

This comment has been minimized.

Copy link

paragones commented Dec 11, 2017

Is there a way to do it with observable (rx java)?

@paragones

This comment has been minimized.

Copy link

paragones commented Dec 18, 2017

I need to do it something like this
private class AuthInterceptor(val auth: AuthenticationComponent) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val originalRequest = chain?.request()!!

        val token = auth.token()

        val newRequest = originalRequest.newBuilder()
                .header("Authorization", "JWT $token")
                .build()

        val response = chain.proceed(newRequest)

        if (response.code() != 200) {
            auth.refreshToken()
                    .subscribe {   }
        } else {
            return response
        }

        Log.e(this.javaClass.simpleName, "response ${response.code().toString()}")
        Log.e(this.javaClass.simpleName, "response.body() ${response.body()}")
        Log.e(this.javaClass.simpleName, "response.headers() ${response.headers()}")

    }
}

auth.refreshToken() is an rxjava call

@Kolyall

This comment has been minimized.

Copy link

Kolyall commented Feb 6, 2018

@jaswanthm how to implement refresh token with Authenticator but avoid multiply token updates from different threads?

@andrconstruction

This comment has been minimized.

Copy link

andrconstruction commented Mar 2, 2018

Hi.How do you implement the logout function within the interceptor? is The intercpetor standalone class or a part of an activity?

@alex-shpak

This comment has been minimized.

Copy link
Owner Author

alex-shpak commented May 22, 2018

@Kolyall
In example synchronized block is used to avoid multiple tokens updates.

@andrconstruction
What kind of logout you need to implement?
Usually it's enough to "forget" api key (and then to not send it to server)

@roni-castro

This comment has been minimized.

Copy link

roni-castro commented Dec 5, 2018

@paragones Did you find a way to refresh the token using rxjava?

@mpetlyuk

This comment has been minimized.

Copy link

mpetlyuk commented Jan 23, 2019

@paragones Did you find a way to refresh the token using rxjava?

https://gist.github.com/mpetlyuk/77ac3221c1776d14654374faf2985d5a
look at my implementation. Include my transformer into each request observable via mehtod Observable.compose(/* refresh transformer*/)

@Tgo1014

This comment has been minimized.

Copy link

Tgo1014 commented Dec 5, 2019

Very useful. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.