Skip to content

Instantly share code, notes, and snippets.

@CarolusX74
Last active March 15, 2022 04:42
Show Gist options
  • Save CarolusX74/057dbf4d8de3095873b15956bdf47935 to your computer and use it in GitHub Desktop.
Save CarolusX74/057dbf4d8de3095873b15956bdf47935 to your computer and use it in GitHub Desktop.
How I automatically refresh OAuth "access token" with okhttp interceptors using "refresh token". All requests will wait until token refresh finished, and then will continue with the new token. - EN (By Carlos Torres)
package com.cjtp.android.api.interceptors;
import android.util.Log;
import com.google.gson.JsonObject;
import com.inviteez.android.core.Session;
import com.inviteez.android.utils.Constant;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import static com.cjtp.android.utils.Constant.TAG_ALIEN;
import static com.cjtp.android.utils.Constant.URL_ENDPOINT_TOKEN_REFRESH;
/**
* Created by CJ on 26/6/2017.
*/
public class AuthTokenRefreshInterceptor implements Interceptor {
private static final String TAG_THIS = AuthTokenRefreshInterceptor.class.getSimpleName();
//--- HTTP Response codes relative constants
private static final int RESPONSE_UNAUTHORIZED_401 = 401;
private static final int RESPONSE_HTTP_RANK_2XX = 2;
private static final int RESPONSE_HTTP_CLIENT_ERROR = 4;
private static final int RESPONSE_HTTP_SERVER_ERROR = 5;
//--- My backend params
private static final String BODY_PARAM_KEY_GRANT_TYPE = "grant_type";
private static final String BODY_PARAM_VALUE_GRANT_TYPE = "refresh_token";
private static final String BODY_PARAM_KEY_REFRESH_TOKEN = "refresh_token";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request(); //<<< Original Request
//Build new request-----------------------------
Request.Builder builder = request.newBuilder();
builder.header("Accept", "application/json"); //if necessary...
String token = Session.getAccessToken(); //Save token of this request for future
setAuthHeader(builder, token); //Add Current Authentication Token..
request = builder.build(); //Overwrite the original request
Log.d(Constant.TAG_ALIEN + TAG_THIS,
">>> Sending Request >>>\n"
+"To: "+request.url()+"\n"
+"Headers:"+request.headers()+"\n"
+"Body: "+bodyToString(request)); //Shows the magic...
//------------------------------------------------------------------------------------------
Response response = chain.proceed(request); // Sends the request (Original w/ Auth.)
//------------------------------------------------------------------------------------------
Log.d(Constant.TAG_ALIEN + TAG_THIS,
"<<< Receiving Request response <<<\n"
+"To: "+response.request().url()+"\n"
+"Headers: "+response.headers()+"\n"
+"Code: "+response.code()+"\n"
+"Body: "+bodyToString(response.request())); //Shows the magic...
//------------------- 401 --- 401 --- UNAUTHORIZED --- 401 --- 401 -------------------------
if (response.code() == RESPONSE_UNAUTHORIZED_401) { //If unauthorized (Token expired)...
Log.w(TAG_ALIEN + TAG_THIS,"Request responses code: "+response.code());
synchronized (this) { // Gets all 401 in sync blocks,
// to avoid multiply token updates...
String currentToken = Session.getAccessToken(); //Get currently stored token (...)
//Compares current token with token that was stored before,
// if it was not updated - do update..
if(currentToken != null && currentToken.equals(token)) {
// --- REFRESHING TOKEN --- --- REFRESHING TOKEN --- --- REFRESHING TOKEN ------
int code = refreshToken() / 100; //Refactor resp. cod ranking
if(code != RESPONSE_HTTP_RANK_2XX) { // If refresh token failed
if(code == RESPONSE_HTTP_CLIENT_ERROR // If failed by error 4xx...
||
code == RESPONSE_HTTP_SERVER_ERROR ){ // If failed by error 5xx...
logout(); // ToDo GoTo login screen
return response; // Todo Shows auth error to user
}
} // <<--------------------------------------------New Auth. Token acquired --
} // <<-----------------------------------New Auth. Token acquired double check --
// --- --- RETRYING ORIGINAL REQUEST --- --- RETRYING ORIGINAL REQUEST --- --------|
if(Session.getAccessToken() != null) { // Checks new Auth. Token
setAuthHeader(builder, Session.getAccessToken()); // Add Current Auth. Token
request = builder.build(); // O/w the original request
Log.d(Constant.TAG_ALIEN + TAG_THIS,
">>> Retrying original Request >>>\n"
+"To: "+request.url()+"\n"
+"Headers:"+request.headers()+"\n"
+"Body: "+bodyToString(request)); //Shows the magic...
//-----------------------------------------------------------------------------|
Response responseRetry = chain.proceed(request);// Sends request (w/ New Auth.)
//-----------------------------------------------------------------------------|
Log.d(Constant.TAG_ALIEN + TAG_THIS,
"<<< Receiving Retried Request response <<<\n"
+"To: "+responseRetry.request().url()+"\n"
+"Headers: "+responseRetry.headers()+"\n"
+"Code: "+responseRetry.code()+"\n"
+"Body: "+bodyToString(response.request())); //Shows the magic.
return responseRetry;
}
}
}else {
//------------------- 200 --- 200 --- AUTHORIZED --- 200 --- 200 -----------------------
Log.w(TAG_ALIEN + TAG_THIS,"Request responses code: "+response.code());
}
return response;
}
// Sets/Adds the authentication header to current request builder.-----------------------------|
private void setAuthHeader(Request.Builder builder, String token) {
Log.i(TAG_ALIEN + TAG_THIS,"Setting authentication header...");
if (token != null){
builder.header("Authorization", String.format("Bearer %s", token));
}
Log.w(TAG_ALIEN + TAG_THIS, "Current Auth Token = "+Session.getAccessToken());
Log.w(TAG_ALIEN + TAG_THIS, "Current Refresh Token = "+Session.getRefreshAccessToken());
}
// Refresh/renew Synchronously Authentication Token & refresh token----------------------------|
private int refreshToken() {
Log.w(TAG_ALIEN+TAG_THIS,"Refreshing tokens... ;o");
// Builds a client...
OkHttpClient client = new OkHttpClient.Builder().build();
// Builds a Request Body...for renewing token...
MediaType jsonType = MediaType.parse("application/json; charset=utf-8");
JsonObject json = new JsonObject();
//---
json.addProperty(BODY_PARAM_KEY_GRANT_TYPE, BODY_PARAM_VALUE_GRANT_TYPE);
json.addProperty(BODY_PARAM_KEY_REFRESH_TOKEN,Session.getRefreshAccessToken());
//---
RequestBody body = RequestBody.create(jsonType,json.toString());
// Builds a request with request body...
Request request = new Request.Builder()
.url(Constant.URL_SERVER+URL_ENDPOINT_TOKEN_REFRESH)
.post(body) //<<<--------------Adds body (Token renew by the way)
.build();
Response response = null;
int code = 0;
Log.d(Constant.TAG_ALIEN + TAG_THIS,
">>> Sending Refresh Token Request >>>\n"
+"To: "+request.url()+"\n"
+"Headers:"+request.headers()+"\n"
+"Body: "+bodyToString(request)); //Shows the magic...
try {
//--------------------------------------------------------------------------------------
response = client.newCall(request).execute(); //Sends Refresh token request
//--------------------------------------------------------------------------------------
Log.d(Constant.TAG_ALIEN + TAG_THIS,
"<<< Receiving Refresh Token Request Response <<<\n"
+"To: "+response.request().url()+"\n"
+"Headers:"+response.headers()+"\n"
+"Code: "+response.code()+"\n"
+"Body: "+bodyToString(response.request())); //Shows the magic...
if (response != null) {
code = response.code();
Log.i(TAG_ALIEN + TAG_THIS,"Token Refresh responses code: "+code);
switch (code){
case 200:
// READS NEW TOKENS AND SAVES THEM -----------------------------------------
try {
//Log.i(TAG_ALIEN+TAG_THIS,"Decoding tokens start");
JSONObject jsonBody = null;
jsonBody = new JSONObject(response.body().string());
String newAuthtoken = jsonBody.getString("access_token");
String tokenRefresh = jsonBody.getString("refresh_token");
Log.i(TAG_ALIEN+TAG_THIS,"New Access Token = "+newAuthtoken);
Log.i(TAG_ALIEN+TAG_THIS,"New Refresh Token = "+tokenRefresh);
Session.setAccessToken(newAuthtoken);
Session.setRefreshAccessToken(tokenRefresh);
//Log.i(TAG_ALIEN+TAG_THIS,"Decoding tokens finish.");
} catch (JSONException e) {
Log.w(TAG_ALIEN + TAG_THIS,"Responses code "+ code
+" but error getting response body.\n"
+ e.getMessage());
}
break;
default:
// READS ERROR -------------------------------------------------------------
try {
//Log.i(TAG_ALIEN+TAG_THIS,"Decoding error start");
JSONObject jsonBodyE = null;
jsonBodyE = new JSONObject(response.body().string());
String error = jsonBodyE.getString("error");
String errorDescription = jsonBodyE.getString("error_description");
//Log.i(TAG_ALIEN+TAG_THIS,"Decoding tokens finish.");
} catch (JSONException e) {
Log.w(TAG_ALIEN + TAG_THIS,"Responses code "+ code
+" but error getting response body.\n"
+ e.getMessage());
e.printStackTrace();
}
break;
}
response.body().close(); //ToDo check this line
}
} catch (IOException e) {
Log.w(TAG_ALIEN + TAG_THIS,"Error while Sending Refresh Token Request\n"+e.getMessage());
e.printStackTrace();
}
//Log.w(TAG_ALIEN,"Refresh Token request responses code? = "+code);
return code;
}
private int logout() {
Log.d(TAG_ALIEN+TAG_THIS,"go to logout");
//logout your user
return 0; //TODO...
}
//----------------------------------------------------------------------------------------------
@Deprecated
private static String bodyToString(final Request request){
/*
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
Log.w(TAG_ALIEN+TAG_THIS,"Error while trying to get body to string.");
return "Null";
}*/
return "Nullix";
}
}
@jaimeDevelopers
Copy link

jaimeDevelopers commented Feb 28, 2019

Hello, I am having some troubles refreshing token on Spotify - Android.
Could you check my question on stackoverflow?
Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment