-
-
Save alex-shpak/da1e65f52dc916716930 to your computer and use it in GitHub Desktop.
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 | |
} | |
} |
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
Authenticator is completely suitable tool for the token refresh action and retry the main request
@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.
@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.
@alex_shpak i have a secured json link with https. Hiw to handle it?
@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.
Thnks @alex-shpak, Helped me out alot.
Hi, suppose i want to inject my SharedPreference (where my current token is stored) using dagger. How to do so ??
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?
Is there a way to do it with observable (rx java)?
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
@jaswanthm how to implement refresh token with Authenticator
but avoid multiply token updates from different threads?
Hi.How do you implement the logout function within the interceptor? is The intercpetor standalone class or a part of an activity?
@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)
@paragones Did you find a way to refresh the token using rxjava?
@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*/)
Very useful. Thanks!
This is very good. Thanks for sharing this.
Only issue with this implementation is after token gets refreshed, all other requests get executed in serial.
Is there a way to make execution of those requests parallel?
private int refreshToken() {
//Refresh token, synchronously, save it, and return result code
//you might use retrofit here
}
So you are proposing to use retrofit inside Interceptor to refresh token? This is quite ugly workaround.
@alouanemed
About server token, I think facebook has their own means to update tokens.
@rajeshkumarkhadka
I think you can use some generic object for locks, but I can't be sure that it will work correct