Skip to content
This repository has been archived by the owner on May 6, 2024. It is now read-only.

Commit

Permalink
Addressed unwanted logout when opening app
Browse files Browse the repository at this point in the history
When there are asynchronous API calls, such as when the app starts,
the token refresh would be called twice resulting in unhandled 401
responses. Logic to retry the request has been added, and the
authenticator is now synchronized.
  • Loading branch information
christopher lee committed Sep 20, 2016
1 parent c75c615 commit f02df2b
Showing 1 changed file with 38 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.google.inject.Inject;
import com.jakewharton.retrofit.Ok3Client;
Expand Down Expand Up @@ -36,6 +37,8 @@ public class OauthRefreshTokenAuthenticator implements Authenticator {

private final Logger logger = new Logger(getClass().getName());
private final static String TOKEN_EXPIRED_ERROR_MESSAGE = "token_expired";
private final static String TOKEN_NONEXISTENT_ERROR_MESSAGE = "token_nonexistent";
private final static String TOKEN_INVALID_GRANT_ERROR_MESSAGE = "invalid_grant";
private Context context;

@Inject
Expand All @@ -44,32 +47,49 @@ public class OauthRefreshTokenAuthenticator implements Authenticator {
@Inject
LoginPrefs loginPrefs;


public OauthRefreshTokenAuthenticator(Context context) {
this.context = context;
RoboGuice.injectMembers(context, this);
}

@Override
public Request authenticate(Route route, final Response response) throws IOException {
public synchronized Request authenticate(Route route, final Response response) throws IOException {
logger.warn(response.toString());

if (!isTokenExpired(response.peekBody(200).string())) {
return null;
}

final AuthResponse currentAuth = loginPrefs.getCurrentAuth();
if (null == currentAuth || null == currentAuth.refresh_token) {
return null;
}
final AuthResponse refreshedAuth;
try {
refreshedAuth = refreshAccessToken(currentAuth);
} catch (HttpException e) {
return null;

String errorCode = getErrorCode(response.peekBody(200).string());

if (errorCode != null) {
switch (errorCode) {
case TOKEN_EXPIRED_ERROR_MESSAGE:
final AuthResponse refreshedAuth;
try {
refreshedAuth = refreshAccessToken(currentAuth);
} catch (HttpException e) {
return null;
}
return response.request().newBuilder()
.header("Authorization", refreshedAuth.token_type + " " + refreshedAuth.access_token)
.build();
case TOKEN_NONEXISTENT_ERROR_MESSAGE:
case TOKEN_INVALID_GRANT_ERROR_MESSAGE:
// Retry request with the current access_token if the original access_token used in
// request does not match the current access_token. This case can occur when
// asynchronous calls are made and are attempting to refresh the access_token where
// one call succeeds but the other fails. https://github.com/edx/edx-app-android/pull/834
if (!response.request().headers().get("Authorization").split(" ")[1].equals(currentAuth.access_token)) {
return response.request().newBuilder()
.header("Authorization", currentAuth.token_type + " " + currentAuth.access_token)
.build();
}
}
}
return response.request().newBuilder()
.header("Authorization", refreshedAuth.token_type + " " + refreshedAuth.access_token)
.build();
return null;
}

@NonNull
Expand All @@ -88,16 +108,14 @@ private AuthResponse refreshAccessToken(AuthResponse currentAuth) throws HttpExc
return refreshTokenResponse;
}

/**
* Checks the if the error_code in the response body is the token_expired error code.
*/
private boolean isTokenExpired(String responseBody) {
@Nullable
private String getErrorCode(String responseBody) {
try {
JSONObject jsonObj = new JSONObject(responseBody);
String errorCode = jsonObj.getString("error_code");
return errorCode.equals(TOKEN_EXPIRED_ERROR_MESSAGE);
return jsonObj.getString("error_code");
} catch (JSONException ex) {
return false;
logger.warn("Unable to get error_code from 401 response");
return null;
}
}
}

0 comments on commit f02df2b

Please sign in to comment.