I'm using Retrofit to handle the communication with the server API, the API user JSON Web Tokens for authentication. The token expires from time to time, and i'm looking for the best way to implement a Retrofit Client that can refresh the token automatically when it expires.
This is the initial implementation i came up with, :
https://gist.github.com/Sserra90/9d87d7926a5c2b7b7541
I don't know the architecture of Retrofit/OkHttpClient deeply, but as far i understand the execute method can be called multiple times from multiple threads, the OkClient is the same shared between Calls only a shallow copy is done. I'm using synchronized in refreshToken()method to avoid multiple threads to enter in refreshToken() and make multiple login calls, i a refresh is needed only one thread should make the refreshCall and the others will use the renewed token.
I haven麓t tested it seriously yet, but for what i can see it's working fine. Maybe someone had this problem already and can share his solution, or it can be helpful for someone with the same/similar problem.
StackOverflow post
http://stackoverflow.com/questions/32926190/retrofit-custom-client-for-webtokens-authentication
Thanks
Hello @Sserra90 it looks like you should try OkHttp interceptors. As soon as, they are powerful enough to inject any logic inside request chain cycle.
public void setup() {
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new TokenInterceptor(tokenManager));
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.baseUrl("http://localhost")
.build();
}
private static class TokenInterceptor implements Interceptor {
private final TokenManager mTokenManager;
private TokenInterceptor(TokenManager tokenManager) {
mTokenManager = tokenManager;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request initialRequest = chain.request();
Request modifiedRequest = request;
if (mTokenManager.hasToken()) {
modifiedRequest = request.newBuilder()
.addHeader("USER_TOKEN", mTokenManager.getToken())
.build();
}
Response response = chain.proceed(modifiedRequest);
boolean unauthorized = response.code() == 401;
if (unauthorized) {
mTokenManager.clearToken();
String newToken = mTokenManager.refreshToken();
modifiedRequest = request.newBuilder()
.addHeader("USER_TOKEN", mTokenManager.getToken())
.build();
return chain.proceed(modifiedRequest);
}
return response;
}
}
interface TokenManager {
String getToken();
boolean hasToken();
void clearToken();
String refreshToken();
}
Also you can look into Authenticator API it was designed to handle such issues.
Another option, is to play around compromise between interceptor and authenticator. Interceptor will capture 401 HttpCode and will try to refresh token, then authenticator will inject missing header inside your request.
Thanks @tomkoptel for the answer, i'll take a look at the Interceptor and Authenticator API i wasn't aware of that, it's a cleaner and more elegant solution than extending the client. I guess you made a typo writing 501 httpCode instead of 401.
@Sserra90 yep, I did. I have updated comment.
Yep, OkHttp interceptors are going to be the way to go. You can use the same simple synchronization mechanisms to in the interceptor to have other pending requests block when a refresh is happening. They too can be run concurrently on multiple threads just like you deduced with Client.execute(). The Client abstract is actually gone in the forthcoming 2.x so you'll have to switch to Interceptor if you want to upgrade.
I'm going to close this issue though since it's neither a bug report of feature request.
Just to add on @tomkoptel comment, if any one reaches to this page like me and looks for the recipe he mentioned "Authenticator" and if finds the link as dead then they the new path for the same is this
Can we do any delayed operation with callback in interceotors? I cannot find anyway of doing that, I need to do a network request in intercepter for refreshing the auth token and then proceed with the actual request after refreshing the token.
Most helpful comment
Hello @Sserra90 it looks like you should try
OkHttpinterceptors. As soon as, they are powerful enough to inject any logic inside request chain cycle.Also you can look into Authenticator API it was designed to handle such issues.
Another option, is to play around compromise between interceptor and authenticator. Interceptor will capture 401 HttpCode and will try to refresh token, then authenticator will inject missing header inside your request.